Package lxml :: Package tests :: Module test_xmlschema
[hide private]
[frames] | no frames]

Source Code for Module lxml.tests.test_xmlschema

  1  # -*- coding: utf-8 -*- 
  2   
  3  """ 
  4  Test cases related to XML Schema parsing and validation 
  5  """ 
  6   
  7  import unittest, sys, os.path 
  8   
  9  this_dir = os.path.dirname(__file__) 
 10  if this_dir not in sys.path: 
 11      sys.path.insert(0, this_dir) # needed for Py3 
 12   
 13  from common_imports import etree, BytesIO, HelperTestCase, fileInTestDir 
 14  from common_imports import doctest, make_doctest 
 15   
 16   
17 -class ETreeXMLSchemaTestCase(HelperTestCase):
18 - def test_xmlschema(self):
19 tree_valid = self.parse('<a><b></b></a>') 20 tree_invalid = self.parse('<a><c></c></a>') 21 schema = self.parse(''' 22 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 23 <xsd:element name="a" type="AType"/> 24 <xsd:complexType name="AType"> 25 <xsd:sequence> 26 <xsd:element name="b" type="xsd:string" /> 27 </xsd:sequence> 28 </xsd:complexType> 29 </xsd:schema> 30 ''') 31 schema = etree.XMLSchema(schema) 32 self.assertTrue(schema.validate(tree_valid)) 33 self.assertFalse(schema.validate(tree_invalid)) 34 self.assertTrue(schema.validate(tree_valid)) # retry valid 35 self.assertFalse(schema.validate(tree_invalid)) # retry invalid
36
37 - def test_xmlschema_error_log(self):
38 tree_valid = self.parse('<a><b></b></a>') 39 tree_invalid = self.parse('<a><c></c></a>') 40 schema = self.parse(''' 41 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 42 <xsd:element name="a" type="AType"/> 43 <xsd:complexType name="AType"> 44 <xsd:sequence> 45 <xsd:element name="b" type="xsd:string" /> 46 </xsd:sequence> 47 </xsd:complexType> 48 </xsd:schema> 49 ''') 50 schema = etree.XMLSchema(schema) 51 self.assertTrue(schema.validate(tree_valid)) 52 self.assertFalse(schema.error_log.filter_from_errors()) 53 54 self.assertFalse(schema.validate(tree_invalid)) 55 self.assertTrue(schema.error_log.filter_from_errors()) 56 self.assertTrue(schema.error_log.filter_types( 57 etree.ErrorTypes.SCHEMAV_ELEMENT_CONTENT)) 58 59 self.assertTrue(schema.validate(tree_valid)) 60 self.assertFalse(schema.error_log.filter_from_errors()) 61 62 self.assertFalse(schema.validate(tree_invalid)) 63 self.assertTrue(schema.error_log.filter_from_errors()) 64 self.assertTrue(schema.error_log.filter_types( 65 etree.ErrorTypes.SCHEMAV_ELEMENT_CONTENT))
66
68 """We don't have a guarantee that there will always be a path 69 for a _LogEntry object (or even a node for which to determina 70 a path), but at least when this test was created schema validation 71 errors always got a node and an XPath value. If that ever changes, 72 we can modify this test to something like: 73 self.assertTrue(error_path is None or tree_path == error_path) 74 That way, we can at least verify that if we did get a path value 75 it wasn't bogus. 76 """ 77 tree = self.parse('<a><b>42</b><b>dada</b></a>') 78 schema = self.parse(''' 79 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 80 <xsd:element name="a" type="AType"/> 81 <xsd:complexType name="AType"> 82 <xsd:sequence> 83 <xsd:element name="b" type="xsd:integer" maxOccurs="2"/> 84 </xsd:sequence> 85 </xsd:complexType> 86 </xsd:schema> 87 ''') 88 schema = etree.XMLSchema(schema) 89 schema.validate(tree) 90 tree_path = tree.getpath(tree.findall('b')[1]) 91 error_path = schema.error_log[0].path 92 self.assertTrue(tree_path == error_path)
93
95 schema = self.parse(''' 96 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 97 <xsd:element name="a" type="AType"/> 98 <xsd:complexType name="AType"> 99 <xsd:sequence minOccurs="4" maxOccurs="4"> 100 <xsd:element name="b" type="BType" /> 101 </xsd:sequence> 102 </xsd:complexType> 103 <xsd:complexType name="BType"> 104 <xsd:attribute name="hardy" type="xsd:string" default="hey" /> 105 </xsd:complexType> 106 </xsd:schema> 107 ''') 108 schema = etree.XMLSchema(schema, attribute_defaults=True) 109 110 tree = self.parse('<a><b hardy="ho"/><b/><b hardy="ho"/><b/></a>') 111 112 root = tree.getroot() 113 self.assertEqual('ho', root[0].get('hardy')) 114 self.assertEqual(None, root[1].get('hardy')) 115 self.assertEqual('ho', root[2].get('hardy')) 116 self.assertEqual(None, root[3].get('hardy')) 117 118 self.assertTrue(schema(tree)) 119 120 root = tree.getroot() 121 self.assertEqual('ho', root[0].get('hardy')) 122 self.assertEqual('hey', root[1].get('hardy')) 123 self.assertEqual('ho', root[2].get('hardy')) 124 self.assertEqual('hey', root[3].get('hardy'))
125
126 - def test_xmlschema_parse(self):
127 schema = self.parse(''' 128 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 129 <xsd:element name="a" type="AType"/> 130 <xsd:complexType name="AType"> 131 <xsd:sequence> 132 <xsd:element name="b" type="xsd:string" /> 133 </xsd:sequence> 134 </xsd:complexType> 135 </xsd:schema> 136 ''') 137 schema = etree.XMLSchema(schema) 138 parser = etree.XMLParser(schema=schema) 139 140 tree_valid = self.parse('<a><b></b></a>', parser=parser) 141 self.assertEqual('a', tree_valid.getroot().tag) 142 143 self.assertRaises(etree.XMLSyntaxError, 144 self.parse, '<a><c></c></a>', parser=parser)
145
147 # does not work as of libxml2 2.7.3 148 schema = self.parse(''' 149 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 150 <xsd:element name="a" type="AType"/> 151 <xsd:complexType name="AType"> 152 <xsd:sequence minOccurs="4" maxOccurs="4"> 153 <xsd:element name="b" type="BType" /> 154 </xsd:sequence> 155 </xsd:complexType> 156 <xsd:complexType name="BType"> 157 <xsd:attribute name="hardy" type="xsd:string" default="hey" /> 158 </xsd:complexType> 159 </xsd:schema> 160 ''') 161 schema = etree.XMLSchema(schema) 162 parser = etree.XMLParser(schema=schema, attribute_defaults=True) 163 164 tree_valid = self.parse('<a><b hardy="ho"/><b/><b hardy="ho"/><b/></a>', 165 parser=parser) 166 root = tree_valid.getroot() 167 self.assertEqual('ho', root[0].get('hardy')) 168 self.assertEqual('hey', root[1].get('hardy')) 169 self.assertEqual('ho', root[2].get('hardy')) 170 self.assertEqual('hey', root[3].get('hardy'))
171
173 # does not work as of libxml2 2.7.3 174 schema = self.parse(''' 175 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 176 <xsd:element name="a" type="AType"/> 177 <xsd:complexType name="AType"> 178 <xsd:sequence minOccurs="4" maxOccurs="4"> 179 <xsd:element name="b" type="BType" /> 180 </xsd:sequence> 181 </xsd:complexType> 182 <xsd:complexType name="BType"> 183 <xsd:attribute name="hardy" type="xsd:string" default="hey" /> 184 </xsd:complexType> 185 </xsd:schema> 186 ''') 187 schema = etree.XMLSchema(schema, attribute_defaults=True) 188 parser = etree.XMLParser(schema=schema) 189 190 tree_valid = self.parse('<a><b hardy="ho"/><b/><b hardy="ho"/><b/></a>', 191 parser=parser) 192 root = tree_valid.getroot() 193 self.assertEqual('ho', root[0].get('hardy')) 194 self.assertEqual('hey', root[1].get('hardy')) 195 self.assertEqual('ho', root[2].get('hardy')) 196 self.assertEqual('hey', root[3].get('hardy'))
197
199 # does not work as of libxml2 2.7.3 200 schema = self.parse(''' 201 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 202 <xsd:element name="a" type="AType"/> 203 <xsd:complexType name="AType"> 204 <xsd:sequence minOccurs="3" maxOccurs="3"> 205 <xsd:element name="b" type="BType" /> 206 </xsd:sequence> 207 </xsd:complexType> 208 <xsd:complexType name="BType"> 209 <xsd:attribute name="hardy" type="xsd:string" fixed="hey" /> 210 </xsd:complexType> 211 </xsd:schema> 212 ''') 213 schema = etree.XMLSchema(schema) 214 parser = etree.XMLParser(schema=schema, attribute_defaults=True) 215 216 tree_valid = self.parse('<a><b/><b hardy="hey"/><b/></a>', 217 parser=parser) 218 root = tree_valid.getroot() 219 self.assertEqual('hey', root[0].get('hardy')) 220 self.assertEqual('hey', root[1].get('hardy')) 221 self.assertEqual('hey', root[2].get('hardy'))
222
223 - def test_xmlschema_stringio(self):
224 schema_file = BytesIO(''' 225 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 226 <xsd:element name="a" type="AType"/> 227 <xsd:complexType name="AType"> 228 <xsd:sequence> 229 <xsd:element name="b" type="xsd:string" /> 230 </xsd:sequence> 231 </xsd:complexType> 232 </xsd:schema> 233 ''') 234 schema = etree.XMLSchema(file=schema_file) 235 parser = etree.XMLParser(schema=schema) 236 237 tree_valid = self.parse('<a><b></b></a>', parser=parser) 238 self.assertEqual('a', tree_valid.getroot().tag) 239 240 self.assertRaises(etree.XMLSyntaxError, 241 self.parse, '<a><c></c></a>', parser=parser)
242
243 - def test_xmlschema_iterparse(self):
244 schema = self.parse(''' 245 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 246 <xsd:element name="a" type="AType"/> 247 <xsd:complexType name="AType"> 248 <xsd:sequence> 249 <xsd:element name="b" type="xsd:string" /> 250 </xsd:sequence> 251 </xsd:complexType> 252 </xsd:schema> 253 ''') 254 schema = etree.XMLSchema(schema) 255 xml = BytesIO('<a><b></b></a>') 256 events = [ (event, el.tag) 257 for (event, el) in etree.iterparse(xml, schema=schema) ] 258 259 self.assertEqual([('end', 'b'), ('end', 'a')], 260 events)
261
263 schema = self.parse(''' 264 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 265 <xsd:element name="a" type="AType"/> 266 <xsd:complexType name="AType"> 267 <xsd:sequence> 268 <xsd:element name="b" type="xsd:string" /> 269 </xsd:sequence> 270 </xsd:complexType> 271 </xsd:schema> 272 ''') 273 schema = etree.XMLSchema(schema) 274 xml = BytesIO('<a><b></b></a>') 275 event, element = next(iter(etree.iterparse(xml, schema=schema))) 276 self.assertEqual('end', event) 277 self.assertEqual('b', element.tag)
278
280 schema = self.parse(''' 281 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 282 <xsd:element name="a" type="AType"/> 283 <xsd:complexType name="AType"> 284 <xsd:sequence> 285 <xsd:element name="b" type="xsd:string" /> 286 </xsd:sequence> 287 </xsd:complexType> 288 </xsd:schema> 289 ''') 290 schema = etree.XMLSchema(schema) 291 self.assertRaises( 292 etree.XMLSyntaxError, 293 list, etree.iterparse(BytesIO('<a><c></c></a>'), schema=schema))
294
296 self.assertRaises(ValueError, etree.XMLSchema, etree.ElementTree())
297
299 self.assertRaises(ValueError, etree.XMLSchema, etree.Comment('TEST'))
300
302 schema = self.parse(''' 303 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 304 <xsd:element name="a" type="xsd:string"/> 305 </xsd:schema> 306 ''') 307 schema = etree.XMLSchema(schema) 308 309 root = etree.Element('a') 310 root.text = 'TEST' 311 self.assertTrue(schema(root)) 312 313 self.assertRaises(ValueError, schema, etree.Comment('TEST')) 314 self.assertRaises(ValueError, schema, etree.PI('a', 'text')) 315 self.assertRaises(ValueError, schema, etree.Entity('text'))
316
318 schema = self.parse('''\ 319 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 320 <element name="a" type="AType"/> 321 <xsd:complexType name="AType"> 322 <xsd:sequence> 323 <xsd:element name="b" type="xsd:string" /> 324 </xsd:sequence> 325 </xsd:complexType> 326 </xsd:schema> 327 ''') 328 self.assertRaises(etree.XMLSchemaParseError, 329 etree.XMLSchema, schema)
330
332 schema = self.parse('<test/>') 333 self.assertRaises(etree.XMLSchemaParseError, 334 etree.XMLSchema, schema)
335
336 - def test_xmlschema_file(self):
337 # this will only work if we access the file through path or 338 # file object.. 339 f = open(fileInTestDir('test.xsd'), 'rb') 340 try: 341 schema = etree.XMLSchema(file=f) 342 finally: 343 f.close() 344 tree_valid = self.parse('<a><b></b></a>') 345 self.assertTrue(schema.validate(tree_valid))
346
348 # this will only work if we access the file through path or 349 # file object.. 350 schema = etree.XMLSchema(file=fileInTestDir('test_import.xsd')) 351 tree_valid = self.parse( 352 '<a:x xmlns:a="http://codespeak.net/lxml/schema/ns1"><b></b></a:x>') 353 self.assertTrue(schema.validate(tree_valid))
354
355 - def test_xmlschema_shortcut(self):
356 tree_valid = self.parse('<a><b></b></a>') 357 tree_invalid = self.parse('<a><c></c></a>') 358 schema = self.parse('''\ 359 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 360 <xsd:element name="a" type="AType"/> 361 <xsd:complexType name="AType"> 362 <xsd:sequence> 363 <xsd:element name="b" type="xsd:string" /> 364 </xsd:sequence> 365 </xsd:complexType> 366 </xsd:schema> 367 ''') 368 self.assertTrue(tree_valid.xmlschema(schema)) 369 self.assertFalse(tree_invalid.xmlschema(schema))
370
372 # this used to crash because the schema part was not properly copied out 373 wsdl = self.parse('''\ 374 <wsdl:definitions 375 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 376 xmlns:xs="http://www.w3.org/2001/XMLSchema"> 377 <wsdl:types> 378 <xs:schema> 379 </xs:schema> 380 </wsdl:types> 381 </wsdl:definitions> 382 ''') 383 schema_element = wsdl.find( 384 "{http://schemas.xmlsoap.org/wsdl/}types/" 385 "{http://www.w3.org/2001/XMLSchema}schema" 386 ) 387 etree.XMLSchema(schema_element) 388 etree.XMLSchema(schema_element) 389 etree.XMLSchema(schema_element)
390 391
392 -class ETreeXMLSchemaResolversTestCase(HelperTestCase):
393 resolver_schema_int = BytesIO("""\ 394 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 395 xmlns:etype="http://codespeak.net/lxml/test/external" 396 targetNamespace="http://codespeak.net/lxml/test/internal"> 397 <xsd:import namespace="http://codespeak.net/lxml/test/external" schemaLocation="XXX.xsd" /> 398 <xsd:element name="a" type="etype:AType"/> 399 </xsd:schema>""") 400 401 resolver_schema_int2 = BytesIO("""\ 402 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 403 xmlns:etype="http://codespeak.net/lxml/test/external" 404 targetNamespace="http://codespeak.net/lxml/test/internal"> 405 <xsd:import namespace="http://codespeak.net/lxml/test/external" schemaLocation="YYY.xsd" /> 406 <xsd:element name="a" type="etype:AType"/> 407 </xsd:schema>""") 408 409 resolver_schema_ext = """\ 410 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 411 targetNamespace="http://codespeak.net/lxml/test/external"> 412 <xsd:complexType name="AType"> 413 <xsd:sequence><xsd:element name="b" type="xsd:string" minOccurs="0" maxOccurs="unbounded" /></xsd:sequence> 414 </xsd:complexType> 415 </xsd:schema>""" 416
417 - class simple_resolver(etree.Resolver):
418 - def __init__(self, schema):
419 self.schema = schema
420
421 - def resolve(self, url, id, context):
422 assert url == 'XXX.xsd' 423 return self.resolve_string(self.schema, context)
424 425 # tests: 426
427 - def test_xmlschema_resolvers(self):
428 # test that resolvers work with schema. 429 parser = etree.XMLParser() 430 parser.resolvers.add(self.simple_resolver(self.resolver_schema_ext)) 431 schema_doc = etree.parse(self.resolver_schema_int, parser = parser) 432 schema = etree.XMLSchema(schema_doc)
433
435 # test that the default resolver will get called if there's no 436 # specific parser resolver. 437 root_resolver = self.simple_resolver(self.resolver_schema_ext) 438 etree.get_default_parser().resolvers.add(root_resolver) 439 schema_doc = etree.parse(self.resolver_schema_int) 440 schema = etree.XMLSchema(schema_doc) 441 etree.get_default_parser().resolvers.remove(root_resolver)
442
444 # test that the default resolver will not get called when a 445 # more specific resolver is registered. 446 447 class res_root(etree.Resolver): 448 def resolve(self, url, id, context): 449 assert False 450 return None
451 452 root_resolver = res_root() 453 etree.get_default_parser().resolvers.add(root_resolver) 454 455 parser = etree.XMLParser() 456 parser.resolvers.add(self.simple_resolver(self.resolver_schema_ext)) 457 458 schema_doc = etree.parse(self.resolver_schema_int, parser = parser) 459 schema = etree.XMLSchema(schema_doc) 460 etree.get_default_parser().resolvers.remove(root_resolver) 461
462 - def test_xmlschema_nested_resolvers(self):
463 # test that resolvers work in a nested fashion. 464 465 resolver_schema = self.resolver_schema_ext 466 467 class res_nested(etree.Resolver): 468 def __init__(self, ext_schema): 469 self.ext_schema = ext_schema
470 471 def resolve(self, url, id, context): 472 assert url == 'YYY.xsd' 473 return self.resolve_string(self.ext_schema, context) 474 475 class res(etree.Resolver): 476 def __init__(self, ext_schema_1, ext_schema_2): 477 self.ext_schema_1 = ext_schema_1 478 self.ext_schema_2 = ext_schema_2 479 480 def resolve(self, url, id, context): 481 assert url == 'XXX.xsd' 482 483 new_parser = etree.XMLParser() 484 new_parser.resolvers.add(res_nested(self.ext_schema_2)) 485 new_schema_doc = etree.parse(self.ext_schema_1, parser = new_parser) 486 new_schema = etree.XMLSchema(new_schema_doc) 487 488 return self.resolve_string(resolver_schema, context) 489 490 parser = etree.XMLParser() 491 parser.resolvers.add(res(self.resolver_schema_int2, self.resolver_schema_ext)) 492 schema_doc = etree.parse(self.resolver_schema_int, parser = parser) 493 schema = etree.XMLSchema(schema_doc) 494 495
496 -def test_suite():
497 suite = unittest.TestSuite() 498 suite.addTests([unittest.makeSuite(ETreeXMLSchemaTestCase)]) 499 suite.addTests([unittest.makeSuite(ETreeXMLSchemaResolversTestCase)]) 500 suite.addTests( 501 [make_doctest('../../../doc/validation.txt')]) 502 return suite
503 504 505 if __name__ == '__main__': 506 print('to test use test.py %s' % __file__) 507