def test_integer_constructors(self): self.wrong_value('xs:integer("hello")', 'FORG0001') self.check_value('xs:integer("19")', 19) self.check_value('xs:integer(xs:untypedAtomic("19"))', 19) self.check_value("xs:integer('-5')", -5) self.wrong_value("xs:integer('INF')", 'FORG0001') self.check_value("xs:integer('inf')", ValueError) self.wrong_value("xs:integer('NaN')", 'FORG0001') self.wrong_value("xs:integer(xs:float('-INF'))", 'FOCA0002') self.check_value("xs:integer(xs:double('NaN'))", ValueError) root = self.etree.XML('<root a="19"/>') context = XPathContext(root) self.check_value('xs:integer(@a)', 19, context=context) root = self.etree.XML('<root/>') context = XPathContext(root, item=float('nan')) self.check_value('xs:integer(.)', ValueError, context=context) self.wrong_value('xs:nonNegativeInteger("-1")') self.wrong_value('xs:nonNegativeInteger(-1)') self.check_value('xs:nonNegativeInteger(0)', 0) self.check_value('xs:nonNegativeInteger(1000)', 1000) self.wrong_value('xs:positiveInteger(0)') self.check_value('xs:positiveInteger("1")', 1) self.wrong_value('xs:negativeInteger(0)') self.check_value('xs:negativeInteger(-1)', -1) self.wrong_value('xs:nonPositiveInteger(1)') self.check_value('xs:nonPositiveInteger(0)', 0) self.check_value('xs:nonPositiveInteger("-1")', -1)
def test_duration_constructor(self): self.check_value('xs:duration("P3Y5M1D")', (41, 86400)) self.check_value('xs:duration(xs:untypedAtomic("P3Y5M1D"))', (41, 86400)) self.check_value('xs:duration("P3Y5M1DT1H")', (41, 90000)) self.check_value('xs:duration("P3Y5M1DT1H3M2.01S")', (41, Decimal('90182.01'))) self.check_value('xs:untypedAtomic("P3Y5M1D") castable as xs:duration', True) self.check_value('"P8192912991912Y" castable as xs:duration', False) self.wrong_value('xs:duration("P3Y5M1X")') self.assertRaises(ValueError, self.parser.parse, 'xs:duration(1)') root = self.etree.XML('<root a="P1Y5M"/>') context = XPathContext(root) self.check_value('xs:duration(@a)', Duration(months=17), context=context) context.item = Duration(months=12, seconds=86400) self.check_value('xs:duration(.)', Duration(12, 86400), context=context) root = self.etree.XML('<root><a>P1Y5M</a></root>') context = XPathContext(root) self.check_value('xs:duration(.)', Duration(months=17), context=context)
def test_gregorian_month_day_constructor(self): tz = Timezone(datetime.timedelta(hours=-14, minutes=0)) self.check_value('xs:gMonthDay("--07-02")', datetime.datetime(2000, 7, 2)) self.check_value('xs:gMonthDay(xs:untypedAtomic("--07-02"))', datetime.datetime(2000, 7, 2)) self.check_value('xs:gMonthDay("--07-02-14:00")', datetime.datetime(2000, 7, 2, tzinfo=tz)) self.check_value('xs:gMonthDay(xs:dateTime("1969-07-20T20:18:00"))', GregorianMonthDay(7, 20)) self.wrong_value('xs:gMonthDay("--7-02")') self.wrong_value('xs:gMonthDay("-07-02")') self.wrong_value('xs:gMonthDay("--07-32")') root = self.etree.XML('<root a="--05-20"/>') context = XPathContext(root) self.check_value('xs:gMonthDay(@a)', GregorianMonthDay(5, 20), context=context) context.item = GregorianMonthDay(1, 15) self.check_value('xs:gMonthDay(.)', GregorianMonthDay(1, 15), context=context)
def test_day_time_duration_constructor(self): self.check_value('xs:dayTimeDuration("-P2DT15H")', DayTimeDuration(seconds=-226800)) self.check_value('xs:dayTimeDuration(xs:duration("-P2DT15H"))', DayTimeDuration(seconds=-226800)) self.check_value('xs:dayTimeDuration("PT240H")', DayTimeDuration.fromstring("P10D")) self.check_value('xs:dayTimeDuration("P365D")', DayTimeDuration.fromstring("P365D")) self.check_value('xs:dayTimeDuration(xs:untypedAtomic("PT240H"))', DayTimeDuration.fromstring("P10D")) self.check_value( 'xs:untypedAtomic("PT240H") castable as xs:dayTimeDuration', True) self.check_value('xs:dayTimeDuration("-P2DT15H0M0S")', DayTimeDuration.fromstring('-P2DT15H')) self.check_value('xs:dayTimeDuration("P3DT10H")', DayTimeDuration.fromstring("P3DT10H")) self.check_value('xs:dayTimeDuration("PT1S")', (0, 1)) self.check_value('xs:dayTimeDuration("PT0S")', (0, 0)) self.wrong_value('xs:dayTimeDuration("+P3DT10H")', 'FORG0001') self.check_value('xs:dayTimeDuration("P999999999999999D")', OverflowError) root = self.etree.XML('<root a="P5DT18H"/>') context = XPathContext(root) self.check_value('xs:dayTimeDuration(@a)', DayTimeDuration(496800), context=context) context.item = DayTimeDuration(86400) self.check_value('xs:dayTimeDuration(.)', DayTimeDuration(86400), context=context)
def test_year_month_duration_constructor(self): self.check_value('xs:yearMonthDuration("P3Y5M")', (41, 0)) self.check_value('xs:yearMonthDuration(xs:untypedAtomic("P3Y5M"))', (41, 0)) self.check_value('xs:yearMonthDuration("-P15M")', (-15, 0)) self.check_value('xs:yearMonthDuration("-P20Y18M")', YearMonthDuration.fromstring("-P21Y6M")) self.check_value('xs:yearMonthDuration(xs:duration("P3Y5M"))', (41, 0)) self.check_value( 'xs:untypedAtomic("P3Y5M") castable as xs:yearMonthDuration', True) self.check_value( '"P9999999999999999Y" castable as xs:yearMonthDuration', False) self.wrong_value('xs:yearMonthDuration("-P15M1D")') self.wrong_value('xs:yearMonthDuration("P15MT1H")') self.wrong_value('xs:yearMonthDuration("P1MT10H")') root = self.etree.XML('<root a="P1Y5M"/>') context = XPathContext(root) self.check_value('xs:yearMonthDuration(@a)', Duration(months=17), context=context) context.item = YearMonthDuration(months=12) self.check_value('xs:yearMonthDuration(.)', YearMonthDuration(12), context=context)
def test_base64_binary_constructor(self): self.check_value('xs:base64Binary(())', []) self.check_value('xs:base64Binary("ODQ=")', b'ODQ=') self.check_value('xs:base64Binary(xs:base64Binary("ODQ="))', b'ODQ=') self.check_value('xs:base64Binary("YWJjZWZnaGk=")', b'YWJjZWZnaGk=') self.wrong_value('xs:base64Binary("xyz")') self.wrong_value('xs:base64Binary("\u0411")') self.wrong_type('xs:base64Binary(1e2)') self.wrong_type('xs:base64Binary(1.1)') root = self.etree.XML('<root a="YWJjZWZnaGk="/>') context = XPathContext(root) self.check_value('xs:base64Binary(@a)', b'YWJjZWZnaGk=', context=context) context.item = UntypedAtomic('YWJjZWZnaGk=') self.check_value('xs:base64Binary(.)', b'YWJjZWZnaGk=', context=context) context.item = b'abcefghi' # Don't change, it can be an encoded value. self.check_value('xs:base64Binary(.)', b'abcefghi', context=context) context.item = b'YWJjZWZnaGlq' self.check_value('xs:base64Binary(.)', b'YWJjZWZnaGlq', context=context)
def check_select(self, path, expected, context=None): """ Checks the materialized result of the *select* method with an XPath expression. The selection is applied on the root token of the parsed XPath expression. :param path: an XPath expression. :param expected: the expected result. Can be a data instance to compare to the result, \ a function that accepts the result as argument and returns a boolean value, an exception \ class that is raised by running the evaluate method. :param context: an optional `XPathContext` instance to be passed to evaluate method. If no \ context is provided the method is called with a dummy context. """ if context is None: context = XPathContext(root=self.etree.Element(u'dummy_root')) else: context = context.copy() root_token = self.parser.parse(path) if isinstance(expected, type) and issubclass(expected, Exception): self.assertRaises(expected, root_token.select, context) elif isinstance(expected, list): self.assertListEqual(list(root_token.select(context)), expected) elif isinstance(expected, set): self.assertEqual(set(root_token.select(context)), expected) elif not callable(expected): self.assertEqual(list(root_token.select(context)), expected) else: self.assertTrue( expected(list(root_token.parse(path).select(context))))
def __call__(self, elem, value=None, source=None, **kwargs): with self._xpath_lock: if not self.parser.is_schema_bound(): self.parser.schema.bind_parser(self.parser) if value is not None: variables = {'value': self.base_type.text_decode(value)} else: variables = {'value': ''} if source is not None: context = XPathContext(root=source.root, item=elem, variables=variables) else: # If validated from a component (could not work with rooted XPath expressions) context = XPathContext(root=elem, variables=variables) try: if not self.token.evaluate(context): msg = "expression is not true with test path %r." yield XMLSchemaValidationError(self, obj=elem, reason=msg % self.path) except ElementPathError as err: yield XMLSchemaValidationError(self, obj=elem, reason=str(err))
def __call__(self, elem, value=None, source=None, namespaces=None, **kwargs): if value is not None: self.parser.variables['value'] = self.base_type.text_decode(value) if not self.parser.is_schema_bound(): self.parser.schema.bind_parser(self.parser) if source is None: context = XPathContext(root=elem) else: context = XPathContext(root=source.root, item=elem) default_namespace = self.parser.namespaces[''] if namespaces and '' in namespaces: self.parser.namespaces[''] = namespaces[''] try: if not self.token.evaluate(context.copy()): msg = "expression is not true with test path %r." yield XMLSchemaValidationError(self, obj=elem, reason=msg % self.path) except ElementPathError as err: yield XMLSchemaValidationError(self, obj=elem, reason=str(err)) self.parser.namespaces[''] = default_namespace
def test_xmlschema_proxy(self): context = XPathContext(root=self.etree.XML( '<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"/>')) self.wrong_syntax("schema-element(*)") self.wrong_name("schema-element(nil)") self.wrong_name("schema-element(xs:string)") self.check_value("schema-element(xs:complexType)", MissingContextError) self.check_value("self::schema-element(xs:complexType)", NameError, context) self.check_value("self::schema-element(xs:schema)", [context.item], context) self.check_tree("schema-element(xs:group)", '(schema-element (: (xs) (group)))') attribute = context.item = AttributeNode(XML_LANG, 'en') self.wrong_syntax("schema-attribute(*)") self.wrong_name("schema-attribute(nil)") self.wrong_name("schema-attribute(xs:string)") self.check_value("schema-attribute(xml:lang)", MissingContextError) self.check_value("schema-attribute(xml:lang)", NameError, context) self.check_value("self::schema-attribute(xml:lang)", [context.item], context) self.check_tree("schema-attribute(xsi:schemaLocation)", '(schema-attribute (: (xsi) (schemaLocation)))') token = self.parser.parse("self::schema-attribute(xml:lang)") context.item = attribute context.axis = 'attribute' self.assertEqual(list(token.select(context)), [context.item])
def test_datetime_constructor(self): tz1 = Timezone(datetime.timedelta(hours=5, minutes=24)) self.check_value('xs:dateTime(())', []) self.check_value('xs:dateTime("1969-07-20T20:18:00")', DateTime10(1969, 7, 20, 20, 18)) self.check_value( 'xs:dateTime(xs:untypedAtomic("1969-07-20T20:18:00"))', DateTime10(1969, 7, 20, 20, 18)) self.check_value( 'xs:dateTime("2000-05-10T21:30:00+05:24")', datetime.datetime(2000, 5, 10, hour=21, minute=30, tzinfo=tz1)) self.check_value('xs:dateTime("1999-12-31T24:00:00")', datetime.datetime(2000, 1, 1, 0, 0)) self.check_value('xs:dateTime(xs:date("1969-07-20"))', DateTime10(1969, 7, 20)) self.check_value('xs:dateTime(xs:date("1969-07-20"))', DateTime10) with self.assertRaises(AssertionError): self.check_value('xs:dateTime(xs:date("1969-07-20"))', DateTime) self.parser._xsd_version = '1.1' try: self.check_value('xs:dateTime(xs:date("1969-07-20"))', DateTime(1969, 7, 20)) self.check_value('xs:dateTime(xs:date("1969-07-20"))', DateTime) finally: self.parser._xsd_version = '1.0' self.wrong_value('xs:dateTime("2000-05-10t21:30:00+05:24")') self.wrong_value('xs:dateTime("2000-5-10T21:30:00+05:24")') self.wrong_value('xs:dateTime("2000-05-10T21:3:00+05:24")') self.wrong_value('xs:dateTime("2000-05-10T21:13:0+05:24")') self.wrong_value('xs:dateTime("2000-05-10T21:13:0")') self.check_value('xs:dateTime("-25252734927766554-12-31T12:00:00")', OverflowError) self.wrong_type('xs:dateTime(50)', 'FORG0006', '1st argument has an invalid type') self.wrong_type('xs:dateTime("2000-05-10T21:30:00", "+05:24")', 'XPST0017') root = self.etree.XML('<root a="1969-07-20T20:18:00"/>') context = XPathContext(root) self.check_value('xs:dateTime(@a)', DateTime10(1969, 7, 20, 20, 18), context=context) context.item = AttributeNode('a', str(DateTime10(1969, 7, 20, 20, 18))) self.check_value('xs:dateTime(.)', DateTime10(1969, 7, 20, 20, 18), context=context) context.item = AttributeNode('a', 'true') self.check_value('xs:dateTime(.)', ValueError, context=context) context.item = DateTime10(1969, 7, 20, 20, 18) self.check_value('xs:dateTime(.)', DateTime10(1969, 7, 20, 20, 18), context=context)
def test_untyped_atomic_constructor(self): self.check_value('xs:untypedAtomic(())', []) root = self.etree.XML('<root>1999</root>') context = XPathContext(root) self.check_value('xs:untypedAtomic(.)', UntypedAtomic(1999), context=context) context.item = UntypedAtomic('true') self.check_value('xs:untypedAtomic(.)', UntypedAtomic(True), context=context)
def test_etree_property(self): root = ElementTree.XML('<root/>') context = XPathContext(root) self.assertEqual(context.etree.__name__, 'xml.etree.ElementTree') self.assertEqual(context.etree.__name__, 'xml.etree.ElementTree') # property caching root = lxml_etree.XML('<root/>') context = XPathContext(root) self.assertEqual(context.etree.__name__, 'lxml.etree') self.assertEqual(context.etree.__name__, 'lxml.etree')
def test_double_constructor(self): self.wrong_value('xs:double("world")') self.check_value('xs:double("39.09")', 39.09) self.check_value('xs:double(xs:untypedAtomic("39.09"))', 39.09) self.check_value('xs:double(-5)', -5.0) self.check_value('xs:double(-5)', float) root = self.etree.XML('<root a="10.3"/>') context = XPathContext(root) context.item = context.root.attributes[0] self.check_value('xs:double(.)', float, context=context) self.check_value('xs:double(.)', 10.3, context=context)
def test_gregorian_month_constructor(self): self.check_value('xs:gMonth("--09")', datetime.datetime(2000, 9, 1)) self.check_value('xs:gMonth(xs:untypedAtomic("--09"))', datetime.datetime(2000, 9, 1)) self.check_value('xs:gMonth("--12")', datetime.datetime(2000, 12, 1)) self.wrong_value('xs:gMonth("--9")') self.wrong_value('xs:gMonth("-09")') self.wrong_value('xs:gMonth("--13")') self.check_value('xs:gMonth(xs:dateTime("1969-07-20T20:18:00"))', GregorianMonth(7)) root = self.etree.XML('<root a="--11"/>') context = XPathContext(root) self.check_value('xs:gMonth(@a)', GregorianMonth(11), context=context) context.item = GregorianMonth(1) self.check_value('xs:gMonth(.)', GregorianMonth(1), context=context)
def iter_select(self, root, **kwargs): context = XPathContext(root, **kwargs) for elem in self.root_token.select_results(context): if not is_etree_element(elem): msg = "XPath expressions on lazy resources can select only elements" raise XMLResourceError(msg) yield elem
def check_statement(self, statement, expected_result, xml_doc=None, context_item=None, context_root=None, debug=False): if xml_doc is None: xml_doc = self.xml_doc if context_root is None: context_root = xml_doc context = XPathContext(root=context_root, item=context_item) statement_root = self.parser.parse(statement) result = statement_root.evaluate(context) if debug: print("[DEBUG] Context root: " + str(context_root)) print("[DEBUG] Context item: " + str(context_item)) print("[DEBUG] Statement: " + str(statement)) print("[DEBUG] Expected: %s (%s)" % (expected_result, str(type(expected_result)))) print("[DEBUG] Got: %s (%s)" % (result, str(type(result)))) self.assertEqual( expected_result, result, "Expected %s for %s" % (str(expected_result), statement))
def test_any_uri_constructor(self): self.check_value('xs:anyURI("")', '') self.check_value('xs:anyURI("https://example.com")', 'https://example.com') self.check_value('xs:anyURI("mailto:[email protected]")', 'mailto:[email protected]') self.check_value('xs:anyURI("urn:example:com")', 'urn:example:com') self.check_value('xs:anyURI(xs:untypedAtomic("urn:example:com"))', 'urn:example:com') self.check_value('xs:anyURI("../principi/libertà.html")', '../principi/libertà.html') self.check_value('xs:anyURI("../principi/libert%E0.html")', '../principi/libert%E0.html') self.check_value('xs:anyURI("../path/page.html#frag")', '../path/page.html#frag') self.wrong_value('xs:anyURI("../path/page.html#frag1#frag2")') self.wrong_value('xs:anyURI("https://example.com/index%.html")') self.wrong_value('xs:anyURI("https://example.com/index.%html")') self.wrong_value('xs:anyURI("https://example.com/index.html% frag")') self.check_value('xs:anyURI(())', []) if platform.python_version_tuple() >= ('3', '6') and \ platform.python_implementation() != 'PyPy': self.wrong_value('xs:anyURI("https://example.com:65536")', 'FORG0001', 'Port out of range 0-65535') root = self.etree.XML('<root a=" https://example.com "/>') context = XPathContext(root) self.check_value('xs:anyURI(@a)', 'https://example.com', context=context)
def select(self, root, **kwargs): context = XPathContext(root, **kwargs) results = self.root_token.get_results(context) if not isinstance(results, list) or any(not is_etree_element(x) for x in results): msg = "XPath expressions on lazy resources can select only elements" raise XMLResourceError(msg) return results
def __call__(self, value): context = XPathContext(self._root, variables={'value': value}) try: if not self.token.evaluate(context): msg = "value is not true with test path %r." yield XMLSchemaValidationError(self, value, reason=msg % self.path) except ElementPathError as err: yield XMLSchemaValidationError(self, value, reason=str(err))
def __call__(self, value): context = XPathContext(self._root, variables={'value': value}) try: if not self.token.evaluate(context): reason = "value is not true with test path {!r}".format(self.path) raise XMLSchemaValidationError(self, value, reason) except ElementPathError as err: raise XMLSchemaValidationError(self, value, reason=str(err)) from None
def test_gregorian_year_month_constructor(self): self.check_value('xs:gYearMonth("2004-02")', datetime.datetime(2004, 2, 1)) self.check_value('xs:gYearMonth(xs:untypedAtomic("2004-02"))', datetime.datetime(2004, 2, 1)) self.check_value('xs:gYearMonth(xs:dateTime("1969-07-20T20:18:00"))', GregorianYearMonth10(1969, 7)) self.wrong_value('xs:gYearMonth("2004-2")') self.wrong_value('xs:gYearMonth("204-02")') self.check_value('"99999999999999999999999999999-01" castable as xs:gYearMonth', False) root = self.etree.XML('<root a="1900-01"/>') context = XPathContext(root) self.check_value('xs:gYearMonth(@a)', GregorianYearMonth10(1900, 1), context=context) context.item = GregorianYearMonth10(1300, 10) self.check_value('xs:gYearMonth(.)', GregorianYearMonth10(1300, 10), context=context)
def test_normalized_string_constructor(self): self.check_value('xs:normalizedString("hello")', "hello") self.check_value('xs:normalizedString(" hello ")', " hello ") self.check_value('xs:normalizedString("\thello \n")', " hello ") self.check_value('xs:normalizedString(())', []) root = self.etree.XML('<root a="\t alpha \n\tbeta "/>') context = XPathContext(root) self.check_value('xs:normalizedString(@a)', ' alpha beta ', context=context)
def test_token_constructor(self): self.check_value('xs:token(" hello world ")', "hello world") self.check_value('xs:token("hello\t world\n")', "hello world") self.check_value('xs:token(xs:untypedAtomic("hello\t world\n"))', "hello world") self.check_value('xs:token(())', []) root = self.etree.XML('<root a=" hello world "/>') context = XPathContext(root) self.check_value('xs:token(@a)', 'hello world', context=context)
def test_time_constructor(self): tz = Timezone(datetime.timedelta(hours=5, minutes=24)) self.check_value('xs:time("21:30:00")', datetime.datetime(2000, 1, 1, 21, 30)) self.check_value('xs:time(xs:untypedAtomic("21:30:00"))', datetime.datetime(2000, 1, 1, 21, 30)) self.check_value('xs:time("11:15:48+05:24")', datetime.datetime(2000, 1, 1, 11, 15, 48, tzinfo=tz)) self.check_value('xs:time(xs:dateTime("1969-07-20T20:18:00"))', Time(20, 18, 00)) self.wrong_value('xs:time("24:00:01")') root = self.etree.XML('<root a="13:15:39"/>') context = XPathContext(root) self.check_value('xs:time(@a)', Time(13, 15, 39), context=context) context.item = Time(20, 10, 00) self.check_value('xs:time(.)', Time(20, 10, 00), context=context)
def __call__(self, elem, value=None, namespaces=None, source=None, **kwargs): with self._xpath_lock: if not self.parser.is_schema_bound(): self.parser.schema.bind_parser(self.parser) variables = {'value': None if value is None else self.base_type.text_decode(value)} if source is not None: context = XPathContext(source.root, namespaces=namespaces, item=elem, variables=variables) else: # If validated from a component (could not work with rooted XPath expressions) context = XPathContext(elem, variables=variables) try: if not self.token.evaluate(context): yield XMLSchemaValidationError(self, obj=elem, reason="assertion test if false") except ElementPathError as err: yield XMLSchemaValidationError(self, obj=elem, reason=str(err))
def test_xmlschema_proxy(self): 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)") self.check_value("schema-element(xs:complexType)", None) 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, 'en') self.wrong_name("schema-attribute(nil)") 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)))')
def assert_deep_eq(self, test_context): output = create_and_run_test(test_context) expression = "fn:deep-equal($result, (%s))" % self.value variables = {'result': output} parser = XPath2Parser(variables=variables) root_node = parser.parse(expression) context = XPathContext(root=etree.XML("<empty/>")) result = root_node.evaluate(context) return result == True
def test_nmtoken_constructor(self): self.check_value('xs:NMTOKEN(" :menù.09-_ ")', ":menù.09-_") self.check_value('xs:NMTOKEN(xs:untypedAtomic(" :menù.09-_ "))', ":menù.09-_") self.wrong_value('xs:NMTOKEN("alpha+")') self.wrong_value('xs:NMTOKEN("hello world")') self.check_value('xs:NMTOKEN(())', []) root = self.etree.XML('<root a=" tns:example "/>') context = XPathContext(root) self.check_value('xs:NMTOKEN(@a)', 'tns:example', context=context)
def test_attributes_type(self): parser = XPath2Parser(namespaces=self.namespaces) token = parser.parse("@min le @max") context = XPathContext(self.etree.XML('<root min="10" max="20" />')) self.assertTrue(token.evaluate(context)) context = XPathContext(self.etree.XML('<root min="10" max="2" />')) self.assertTrue(token.evaluate(context)) schema = xmlschema.XMLSchema(''' <xs:schema xmlns="http://xpath.test/ns" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://xpath.test/ns"> <xs:element name="range" type="intRange"/> <xs:complexType name="intRange"> <xs:attribute name="min" type="xs:int"/> <xs:attribute name="max" type="xs:int"/> </xs:complexType> </xs:schema>''') parser = XPath2Parser(namespaces=self.namespaces, schema=XMLSchemaProxy(schema, schema.elements['range'])) token = parser.parse("@min le @max") context = XPathContext(self.etree.XML('<root min="10" max="20" />')) self.assertTrue(token.evaluate(context)) context = XPathContext(self.etree.XML('<root min="10" max="2" />')) self.assertFalse(token.evaluate(context)) schema = xmlschema.XMLSchema(''' <xs:schema xmlns="http://xpath.test/ns" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://xpath.test/ns"> <xs:element name="range" type="intRange"/> <xs:complexType name="intRange"> <xs:attribute name="min" type="xs:int"/> <xs:attribute name="max" type="xs:string"/> </xs:complexType> </xs:schema>''') parser = XPath2Parser(namespaces=self.namespaces, schema=XMLSchemaProxy(schema, schema.elements['range'])) self.assertRaises(TypeError, parser.parse, '@min le @max')