selftest.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  1. # $Id: selftest.py 2326 2005-03-17 07:45:21Z fredrik $
  2. # -*- coding: iso-8859-1 -*-
  3. # elementtree selftest program
  4. # this test script uses Python's "doctest" module to check that the
  5. # *test script* works as expected.
  6. # TODO: add more elementtree method tests
  7. # TODO: add xml/html parsing tests
  8. # TODO: etc
  9. import sys, string, StringIO
  10. from elementtree import ElementTree
  11. from elementtree import ElementPath
  12. from elementtree import ElementInclude
  13. from elementtree import HTMLTreeBuilder
  14. from elementtree import SimpleXMLWriter
  15. def serialize(elem, encoding=None):
  16. import StringIO
  17. file = StringIO.StringIO()
  18. tree = ElementTree.ElementTree(elem)
  19. if encoding:
  20. tree.write(file, encoding)
  21. else:
  22. tree.write(file)
  23. return file.getvalue()
  24. def summarize(elem):
  25. return elem.tag
  26. def summarize_list(seq):
  27. return map(summarize, seq)
  28. def normalize_crlf(tree):
  29. for elem in tree.getiterator():
  30. if elem.text: elem.text = string.replace(elem.text, "\r\n", "\n")
  31. if elem.tail: elem.tail = string.replace(elem.tail, "\r\n", "\n")
  32. SAMPLE_XML = ElementTree.XML("""
  33. <body>
  34. <tag>text</tag>
  35. <tag />
  36. <section>
  37. <tag>subtext</tag>
  38. </section>
  39. </body>
  40. """)
  41. #
  42. # interface tests
  43. def check_string(string):
  44. len(string)
  45. for char in string:
  46. if len(char) != 1:
  47. print "expected one-character string, got %r" % char
  48. new_string = string + ""
  49. new_string = string + " "
  50. string[:0]
  51. def check_string_or_none(value):
  52. if value is None:
  53. return
  54. return check_string(value)
  55. def check_mapping(mapping):
  56. len(mapping)
  57. keys = mapping.keys()
  58. items = mapping.items()
  59. for key in keys:
  60. item = mapping[key]
  61. mapping["key"] = "value"
  62. if mapping["key"] != "value":
  63. print "expected value string, got %r" % mapping["key"]
  64. def check_element(element):
  65. if not hasattr(element, "tag"):
  66. print "no tag member"
  67. if not hasattr(element, "attrib"):
  68. print "no attrib member"
  69. if not hasattr(element, "text"):
  70. print "no text member"
  71. if not hasattr(element, "tail"):
  72. print "no tail member"
  73. check_string(element.tag)
  74. check_mapping(element.attrib)
  75. check_string_or_none(element.text)
  76. check_string_or_none(element.tail)
  77. for elem in element:
  78. check_element(elem)
  79. def check_element_tree(tree):
  80. check_element(tree.getroot())
  81. # --------------------------------------------------------------------
  82. # element tree tests
  83. def sanity():
  84. """
  85. >>> from elementtree.ElementTree import *
  86. >>> from elementtree.ElementInclude import *
  87. >>> from elementtree.ElementPath import *
  88. >>> from elementtree.HTMLTreeBuilder import *
  89. >>> from elementtree.SimpleXMLTreeBuilder import *
  90. >>> from elementtree.SimpleXMLWriter import *
  91. >>> from elementtree.TidyTools import *
  92. >>> from elementtree.XMLTreeBuilder import *
  93. """
  94. def version():
  95. """
  96. >>> ElementTree.VERSION
  97. '1.2.6'
  98. """
  99. def interface():
  100. """
  101. Test element tree interface.
  102. >>> element = ElementTree.Element("tag")
  103. >>> check_element(element)
  104. >>> tree = ElementTree.ElementTree(element)
  105. >>> check_element_tree(tree)
  106. """
  107. def simplefind():
  108. """
  109. Test find methods using the elementpath fallback.
  110. >>> CurrentElementPath = ElementTree.ElementPath
  111. >>> ElementTree.ElementPath = ElementTree._SimpleElementPath()
  112. >>> elem = SAMPLE_XML
  113. >>> elem.find("tag").tag
  114. 'tag'
  115. >>> ElementTree.ElementTree(elem).find("tag").tag
  116. 'tag'
  117. >>> elem.findtext("tag")
  118. 'text'
  119. >>> elem.findtext("tog")
  120. >>> elem.findtext("tog", "default")
  121. 'default'
  122. >>> ElementTree.ElementTree(elem).findtext("tag")
  123. 'text'
  124. >>> summarize_list(elem.findall("tag"))
  125. ['tag', 'tag']
  126. >>> summarize_list(elem.findall(".//tag"))
  127. ['tag', 'tag', 'tag']
  128. Path syntax doesn't work in this case.
  129. >>> elem.find("section/tag")
  130. >>> elem.findtext("section/tag")
  131. >>> elem.findall("section/tag")
  132. []
  133. >>> ElementTree.ElementPath = CurrentElementPath
  134. """
  135. def find():
  136. """
  137. Test find methods (including xpath syntax).
  138. >>> elem = SAMPLE_XML
  139. >>> elem.find("tag").tag
  140. 'tag'
  141. >>> ElementTree.ElementTree(elem).find("tag").tag
  142. 'tag'
  143. >>> elem.find("section/tag").tag
  144. 'tag'
  145. >>> ElementTree.ElementTree(elem).find("section/tag").tag
  146. 'tag'
  147. >>> elem.findtext("tag")
  148. 'text'
  149. >>> elem.findtext("tog")
  150. >>> elem.findtext("tog", "default")
  151. 'default'
  152. >>> ElementTree.ElementTree(elem).findtext("tag")
  153. 'text'
  154. >>> elem.findtext("section/tag")
  155. 'subtext'
  156. >>> ElementTree.ElementTree(elem).findtext("section/tag")
  157. 'subtext'
  158. >>> summarize_list(elem.findall("tag"))
  159. ['tag', 'tag']
  160. >>> summarize_list(elem.findall("*"))
  161. ['tag', 'tag', 'section']
  162. >>> summarize_list(elem.findall(".//tag"))
  163. ['tag', 'tag', 'tag']
  164. >>> summarize_list(elem.findall("section/tag"))
  165. ['tag']
  166. >>> summarize_list(elem.findall("section//tag"))
  167. ['tag']
  168. >>> summarize_list(elem.findall("section/*"))
  169. ['tag']
  170. >>> summarize_list(elem.findall("section//*"))
  171. ['tag']
  172. >>> summarize_list(elem.findall("section/.//*"))
  173. ['tag']
  174. >>> summarize_list(elem.findall("*/*"))
  175. ['tag']
  176. >>> summarize_list(elem.findall("*//*"))
  177. ['tag']
  178. >>> summarize_list(elem.findall("*/tag"))
  179. ['tag']
  180. >>> summarize_list(elem.findall("*/./tag"))
  181. ['tag']
  182. >>> summarize_list(elem.findall("./tag"))
  183. ['tag', 'tag']
  184. >>> summarize_list(elem.findall(".//tag"))
  185. ['tag', 'tag', 'tag']
  186. >>> summarize_list(elem.findall("././tag"))
  187. ['tag', 'tag']
  188. >>> summarize_list(ElementTree.ElementTree(elem).findall("/tag"))
  189. ['tag', 'tag']
  190. >>> summarize_list(ElementTree.ElementTree(elem).findall("./tag"))
  191. ['tag', 'tag']
  192. """
  193. def bad_find():
  194. """
  195. Check bad or unsupported path expressions.
  196. >>> elem = SAMPLE_XML
  197. >>> elem.findall("/tag")
  198. Traceback (most recent call last):
  199. SyntaxError: cannot use absolute path on element
  200. >>> elem.findall("../tag")
  201. Traceback (most recent call last):
  202. SyntaxError: unsupported path syntax (..)
  203. >>> elem.findall("section//")
  204. Traceback (most recent call last):
  205. SyntaxError: path cannot end with //
  206. >>> elem.findall("tag[tag]")
  207. Traceback (most recent call last):
  208. SyntaxError: expected path separator ([)
  209. """
  210. def parsefile():
  211. """
  212. Test parsing from file.
  213. >>> tree = ElementTree.parse("samples/simple.xml")
  214. >>> normalize_crlf(tree)
  215. >>> tree.write(sys.stdout)
  216. <root>
  217. <element key="value">text</element>
  218. <element>text</element>tail
  219. <empty-element />
  220. </root>
  221. >>> tree = ElementTree.parse("samples/simple-ns.xml")
  222. >>> normalize_crlf(tree)
  223. >>> tree.write(sys.stdout)
  224. <ns0:root xmlns:ns0="namespace">
  225. <ns0:element key="value">text</ns0:element>
  226. <ns0:element>text</ns0:element>tail
  227. <ns0:empty-element />
  228. </ns0:root>
  229. """
  230. def parsehtml():
  231. """
  232. Test HTML parsing.
  233. >>> p = HTMLTreeBuilder.TreeBuilder()
  234. >>> p.feed("<p><p>spam<b>egg</b></p>")
  235. >>> serialize(p.close())
  236. '<p>spam<b>egg</b></p>'
  237. """
  238. def parseliteral():
  239. r"""
  240. >>> element = ElementTree.XML("<html><body>text</body></html>")
  241. >>> ElementTree.ElementTree(element).write(sys.stdout)
  242. <html><body>text</body></html>
  243. >>> element = ElementTree.fromstring("<html><body>text</body></html>")
  244. >>> ElementTree.ElementTree(element).write(sys.stdout)
  245. <html><body>text</body></html>
  246. >>> print ElementTree.tostring(element)
  247. <html><body>text</body></html>
  248. >>> print ElementTree.tostring(element, "ascii")
  249. <?xml version='1.0' encoding='ascii'?>
  250. <html><body>text</body></html>
  251. >>> _, ids = ElementTree.XMLID("<html><body>text</body></html>")
  252. >>> len(ids)
  253. 0
  254. >>> _, ids = ElementTree.XMLID("<html><body id='body'>text</body></html>")
  255. >>> len(ids)
  256. 1
  257. >>> ids["body"].tag
  258. 'body'
  259. """
  260. def simpleparsefile():
  261. """
  262. Test the xmllib-based parser.
  263. >>> from elementtree import SimpleXMLTreeBuilder
  264. >>> parser = SimpleXMLTreeBuilder.TreeBuilder()
  265. >>> tree = ElementTree.parse("samples/simple.xml", parser)
  266. >>> normalize_crlf(tree)
  267. >>> tree.write(sys.stdout)
  268. <root>
  269. <element key="value">text</element>
  270. <element>text</element>tail
  271. <empty-element />
  272. </root>
  273. """
  274. def iterparse():
  275. """
  276. Test iterparse interface.
  277. >>> iterparse = ElementTree.iterparse
  278. >>> context = iterparse("samples/simple.xml")
  279. >>> for action, elem in context:
  280. ... print action, elem.tag
  281. end element
  282. end element
  283. end empty-element
  284. end root
  285. >>> context.root.tag
  286. 'root'
  287. >>> context = iterparse("samples/simple-ns.xml")
  288. >>> for action, elem in context:
  289. ... print action, elem.tag
  290. end {namespace}element
  291. end {namespace}element
  292. end {namespace}empty-element
  293. end {namespace}root
  294. >>> events = ()
  295. >>> context = iterparse("samples/simple.xml", events)
  296. >>> for action, elem in context:
  297. ... print action, elem.tag
  298. >>> events = ()
  299. >>> context = iterparse("samples/simple.xml", events=events)
  300. >>> for action, elem in context:
  301. ... print action, elem.tag
  302. >>> events = ("start", "end")
  303. >>> context = iterparse("samples/simple.xml", events)
  304. >>> for action, elem in context:
  305. ... print action, elem.tag
  306. start root
  307. start element
  308. end element
  309. start element
  310. end element
  311. start empty-element
  312. end empty-element
  313. end root
  314. >>> events = ("start", "end", "start-ns", "end-ns")
  315. >>> context = iterparse("samples/simple-ns.xml", events)
  316. >>> for action, elem in context:
  317. ... if action in ("start", "end"):
  318. ... print action, elem.tag
  319. ... else:
  320. ... print action, elem
  321. start-ns ('', 'namespace')
  322. start {namespace}root
  323. start {namespace}element
  324. end {namespace}element
  325. start {namespace}element
  326. end {namespace}element
  327. start {namespace}empty-element
  328. end {namespace}empty-element
  329. end {namespace}root
  330. end-ns None
  331. """
  332. def fancyparsefile():
  333. """
  334. Test the "fancy" parser.
  335. Sanity check.
  336. >>> from elementtree import XMLTreeBuilder
  337. >>> parser = XMLTreeBuilder.FancyTreeBuilder()
  338. >>> tree = ElementTree.parse("samples/simple.xml", parser)
  339. >>> normalize_crlf(tree)
  340. >>> tree.write(sys.stdout)
  341. <root>
  342. <element key="value">text</element>
  343. <element>text</element>tail
  344. <empty-element />
  345. </root>
  346. Callback check.
  347. >>> class MyFancyParser(XMLTreeBuilder.FancyTreeBuilder):
  348. ... def start(self, elem):
  349. ... print "START", elem.tag
  350. ... def end(self, elem):
  351. ... print "END", elem.tag
  352. >>> parser = MyFancyParser()
  353. >>> tree = ElementTree.parse("samples/simple.xml", parser)
  354. START root
  355. START element
  356. END element
  357. START element
  358. END element
  359. START empty-element
  360. END empty-element
  361. END root
  362. """
  363. def writefile():
  364. """
  365. >>> elem = ElementTree.Element("tag")
  366. >>> elem.text = "text"
  367. >>> serialize(elem)
  368. '<tag>text</tag>'
  369. >>> ElementTree.SubElement(elem, "subtag").text = "subtext"
  370. >>> serialize(elem)
  371. '<tag>text<subtag>subtext</subtag></tag>'
  372. """
  373. def writestring():
  374. """
  375. >>> elem = ElementTree.XML("<html><body>text</body></html>")
  376. >>> ElementTree.tostring(elem)
  377. '<html><body>text</body></html>'
  378. >>> elem = ElementTree.fromstring("<html><body>text</body></html>")
  379. >>> ElementTree.tostring(elem)
  380. '<html><body>text</body></html>'
  381. """
  382. def encoding():
  383. r"""
  384. Test encoding issues.
  385. >>> elem = ElementTree.Element("tag")
  386. >>> elem.text = u"abc"
  387. >>> serialize(elem)
  388. '<tag>abc</tag>'
  389. >>> serialize(elem, "utf-8")
  390. '<tag>abc</tag>'
  391. >>> serialize(elem, "us-ascii")
  392. '<tag>abc</tag>'
  393. >>> serialize(elem, "iso-8859-1")
  394. "<?xml version='1.0' encoding='iso-8859-1'?>\n<tag>abc</tag>"
  395. >>> elem.text = "<&\"\'>"
  396. >>> serialize(elem)
  397. '<tag>&lt;&amp;"\'&gt;</tag>'
  398. >>> serialize(elem, "utf-8")
  399. '<tag>&lt;&amp;"\'&gt;</tag>'
  400. >>> serialize(elem, "us-ascii") # cdata characters
  401. '<tag>&lt;&amp;"\'&gt;</tag>'
  402. >>> serialize(elem, "iso-8859-1")
  403. '<?xml version=\'1.0\' encoding=\'iso-8859-1\'?>\n<tag>&lt;&amp;"\'&gt;</tag>'
  404. >>> elem.attrib["key"] = "<&\"\'>"
  405. >>> elem.text = None
  406. >>> serialize(elem)
  407. '<tag key="&lt;&amp;&quot;&apos;&gt;" />'
  408. >>> serialize(elem, "utf-8")
  409. '<tag key="&lt;&amp;&quot;&apos;&gt;" />'
  410. >>> serialize(elem, "us-ascii")
  411. '<tag key="&lt;&amp;&quot;&apos;&gt;" />'
  412. >>> serialize(elem, "iso-8859-1")
  413. '<?xml version=\'1.0\' encoding=\'iso-8859-1\'?>\n<tag key="&lt;&amp;&quot;&apos;&gt;" />'
  414. >>> elem.text = u'\xe5\xf6\xf6<>'
  415. >>> elem.attrib.clear()
  416. >>> serialize(elem)
  417. '<tag>&#229;&#246;&#246;&lt;&gt;</tag>'
  418. >>> serialize(elem, "utf-8")
  419. '<tag>\xc3\xa5\xc3\xb6\xc3\xb6&lt;&gt;</tag>'
  420. >>> serialize(elem, "us-ascii")
  421. '<tag>&#229;&#246;&#246;&lt;&gt;</tag>'
  422. >>> serialize(elem, "iso-8859-1")
  423. "<?xml version='1.0' encoding='iso-8859-1'?>\n<tag>\xe5\xf6\xf6&lt;&gt;</tag>"
  424. >>> elem.attrib["key"] = u'\xe5\xf6\xf6<>'
  425. >>> elem.text = None
  426. >>> serialize(elem)
  427. '<tag key="&#229;&#246;&#246;&lt;&gt;" />'
  428. >>> serialize(elem, "utf-8")
  429. '<tag key="\xc3\xa5\xc3\xb6\xc3\xb6&lt;&gt;" />'
  430. >>> serialize(elem, "us-ascii")
  431. '<tag key="&#229;&#246;&#246;&lt;&gt;" />'
  432. >>> serialize(elem, "iso-8859-1")
  433. '<?xml version=\'1.0\' encoding=\'iso-8859-1\'?>\n<tag key="\xe5\xf6\xf6&lt;&gt;" />'
  434. """
  435. ENTITY_XML = """\
  436. <!DOCTYPE points [
  437. <!ENTITY % user-entities SYSTEM 'user-entities.xml'>
  438. %user-entities;
  439. ]>
  440. <document>&entity;</document>
  441. """
  442. def entity():
  443. """
  444. Test entity handling.
  445. 1) bad entities
  446. >>> ElementTree.XML("<document>&entity;</document>")
  447. Traceback (most recent call last):
  448. ExpatError: undefined entity: line 1, column 10
  449. >>> ElementTree.XML(ENTITY_XML)
  450. Traceback (most recent call last):
  451. ExpatError: undefined entity &entity;: line 5, column 10
  452. (add more tests here)
  453. """
  454. def namespace():
  455. """
  456. Test namespace issues.
  457. 1) xml namespace
  458. >>> elem = ElementTree.XML("<tag xml:lang='en' />")
  459. >>> serialize(elem) # 1.1
  460. '<tag xml:lang="en" />'
  461. 2) other "well-known" namespaces
  462. >>> elem = ElementTree.XML("<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' />")
  463. >>> serialize(elem) # 2.1
  464. '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" />'
  465. >>> elem = ElementTree.XML("<html:html xmlns:html='http://www.w3.org/1999/xhtml' />")
  466. >>> serialize(elem) # 2.2
  467. '<html:html xmlns:html="http://www.w3.org/1999/xhtml" />'
  468. >>> elem = ElementTree.XML("<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope' />")
  469. >>> serialize(elem) # 2.3
  470. '<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope" />'
  471. 3) unknown namespaces
  472. """
  473. def qname():
  474. """
  475. Test QName handling.
  476. 1) decorated tags
  477. >>> elem = ElementTree.Element("{uri}tag")
  478. >>> serialize(elem) # 1.1
  479. '<ns0:tag xmlns:ns0="uri" />'
  480. >>> elem = ElementTree.Element(ElementTree.QName("{uri}tag"))
  481. >>> serialize(elem) # 1.2
  482. '<ns0:tag xmlns:ns0="uri" />'
  483. >>> elem = ElementTree.Element(ElementTree.QName("uri", "tag"))
  484. >>> serialize(elem) # 1.3
  485. '<ns0:tag xmlns:ns0="uri" />'
  486. 2) decorated attributes
  487. >>> elem.clear()
  488. >>> elem.attrib["{uri}key"] = "value"
  489. >>> serialize(elem) # 2.1
  490. '<ns0:tag ns0:key="value" xmlns:ns0="uri" />'
  491. >>> elem.clear()
  492. >>> elem.attrib[ElementTree.QName("{uri}key")] = "value"
  493. >>> serialize(elem) # 2.2
  494. '<ns0:tag ns0:key="value" xmlns:ns0="uri" />'
  495. 3) decorated values are not converted by default, but the
  496. QName wrapper can be used for values
  497. >>> elem.clear()
  498. >>> elem.attrib["{uri}key"] = "{uri}value"
  499. >>> serialize(elem) # 3.1
  500. '<ns0:tag ns0:key="{uri}value" xmlns:ns0="uri" />'
  501. >>> elem.clear()
  502. >>> elem.attrib["{uri}key"] = ElementTree.QName("{uri}value")
  503. >>> serialize(elem) # 3.2
  504. '<ns0:tag ns0:key="ns0:value" xmlns:ns0="uri" />'
  505. >>> elem.clear()
  506. >>> subelem = ElementTree.Element("tag")
  507. >>> subelem.attrib["{uri1}key"] = ElementTree.QName("{uri2}value")
  508. >>> elem.append(subelem)
  509. >>> elem.append(subelem)
  510. >>> serialize(elem) # 3.3
  511. '<ns0:tag xmlns:ns0="uri"><tag ns1:key="ns2:value" xmlns:ns1="uri1" xmlns:ns2="uri2" /><tag ns1:key="ns2:value" xmlns:ns1="uri1" xmlns:ns2="uri2" /></ns0:tag>'
  512. """
  513. def xpath_tokenizer(p):
  514. """
  515. Test the XPath tokenizer.
  516. >>> # tests from the xml specification
  517. >>> xpath_tokenizer("*")
  518. ['*']
  519. >>> xpath_tokenizer("text()")
  520. ['text', '()']
  521. >>> xpath_tokenizer("@name")
  522. ['@', 'name']
  523. >>> xpath_tokenizer("@*")
  524. ['@', '*']
  525. >>> xpath_tokenizer("para[1]")
  526. ['para', '[', '1', ']']
  527. >>> xpath_tokenizer("para[last()]")
  528. ['para', '[', 'last', '()', ']']
  529. >>> xpath_tokenizer("*/para")
  530. ['*', '/', 'para']
  531. >>> xpath_tokenizer("/doc/chapter[5]/section[2]")
  532. ['/', 'doc', '/', 'chapter', '[', '5', ']', '/', 'section', '[', '2', ']']
  533. >>> xpath_tokenizer("chapter//para")
  534. ['chapter', '/', '/', 'para']
  535. >>> xpath_tokenizer("//para")
  536. ['/', '/', 'para']
  537. >>> xpath_tokenizer("//olist/item")
  538. ['/', '/', 'olist', '/', 'item']
  539. >>> xpath_tokenizer(".")
  540. ['.']
  541. >>> xpath_tokenizer(".//para")
  542. ['.', '/', '/', 'para']
  543. >>> xpath_tokenizer("..")
  544. ['..']
  545. >>> xpath_tokenizer("../@lang")
  546. ['..', '/', '@', 'lang']
  547. >>> xpath_tokenizer("chapter[title]")
  548. ['chapter', '[', 'title', ']']
  549. >>> xpath_tokenizer("employee[@secretary and @assistant]")
  550. ['employee', '[', '@', 'secretary', '', 'and', '', '@', 'assistant', ']']
  551. >>> # additional tests
  552. >>> xpath_tokenizer("{http://spam}egg")
  553. ['{http://spam}egg']
  554. >>> xpath_tokenizer("./spam.egg")
  555. ['.', '/', 'spam.egg']
  556. >>> xpath_tokenizer(".//{http://spam}egg")
  557. ['.', '/', '/', '{http://spam}egg']
  558. """
  559. out = []
  560. for op, tag in ElementPath.xpath_tokenizer(p):
  561. out.append(op or tag)
  562. return out
  563. #
  564. # xinclude tests (samples from appendix C of the xinclude specification)
  565. XINCLUDE = {}
  566. XINCLUDE["C1.xml"] = """\
  567. <?xml version='1.0'?>
  568. <document xmlns:xi="http://www.w3.org/2001/XInclude">
  569. <p>120 Mz is adequate for an average home user.</p>
  570. <xi:include href="disclaimer.xml"/>
  571. </document>
  572. """
  573. XINCLUDE["disclaimer.xml"] = """\
  574. <?xml version='1.0'?>
  575. <disclaimer>
  576. <p>The opinions represented herein represent those of the individual
  577. and should not be interpreted as official policy endorsed by this
  578. organization.</p>
  579. </disclaimer>
  580. """
  581. XINCLUDE["C2.xml"] = """\
  582. <?xml version='1.0'?>
  583. <document xmlns:xi="http://www.w3.org/2001/XInclude">
  584. <p>This document has been accessed
  585. <xi:include href="count.txt" parse="text"/> times.</p>
  586. </document>
  587. """
  588. XINCLUDE["count.txt"] = "324387"
  589. XINCLUDE["C3.xml"] = """\
  590. <?xml version='1.0'?>
  591. <document xmlns:xi="http://www.w3.org/2001/XInclude">
  592. <p>The following is the source of the "data.xml" resource:</p>
  593. <example><xi:include href="data.xml" parse="text"/></example>
  594. </document>
  595. """
  596. XINCLUDE["data.xml"] = """\
  597. <?xml version='1.0'?>
  598. <data>
  599. <item><![CDATA[Brooks & Shields]]></item>
  600. </data>
  601. """
  602. XINCLUDE["C5.xml"] = """\
  603. <?xml version='1.0'?>
  604. <div xmlns:xi="http://www.w3.org/2001/XInclude">
  605. <xi:include href="example.txt" parse="text">
  606. <xi:fallback>
  607. <xi:include href="fallback-example.txt" parse="text">
  608. <xi:fallback><a href="mailto:bob@example.org">Report error</a></xi:fallback>
  609. </xi:include>
  610. </xi:fallback>
  611. </xi:include>
  612. </div>
  613. """
  614. XINCLUDE["default.xml"] = """\
  615. <?xml version='1.0'?>
  616. <document xmlns:xi="http://www.w3.org/2001/XInclude">
  617. <p>Example.</p>
  618. <xi:include href="samples/simple.xml"/>
  619. </document>
  620. """
  621. def xinclude_loader(href, parse="xml", encoding=None):
  622. try:
  623. data = XINCLUDE[href]
  624. except KeyError:
  625. raise IOError("resource not found")
  626. if parse == "xml":
  627. return ElementTree.XML(data)
  628. return data
  629. def xinclude():
  630. r"""
  631. Basic inclusion example (XInclude C.1)
  632. >>> document = xinclude_loader("C1.xml")
  633. >>> ElementInclude.include(document, xinclude_loader)
  634. >>> print serialize(document) # C1
  635. <document>
  636. <p>120 Mz is adequate for an average home user.</p>
  637. <disclaimer>
  638. <p>The opinions represented herein represent those of the individual
  639. and should not be interpreted as official policy endorsed by this
  640. organization.</p>
  641. </disclaimer>
  642. </document>
  643. Textual inclusion example (XInclude C.2)
  644. >>> document = xinclude_loader("C2.xml")
  645. >>> ElementInclude.include(document, xinclude_loader)
  646. >>> print serialize(document) # C2
  647. <document>
  648. <p>This document has been accessed
  649. 324387 times.</p>
  650. </document>
  651. Textual inclusion of XML example (XInclude C.3)
  652. >>> document = xinclude_loader("C3.xml")
  653. >>> ElementInclude.include(document, xinclude_loader)
  654. >>> print serialize(document) # C3
  655. <document>
  656. <p>The following is the source of the "data.xml" resource:</p>
  657. <example>&lt;?xml version='1.0'?&gt;
  658. &lt;data&gt;
  659. &lt;item&gt;&lt;![CDATA[Brooks &amp; Shields]]&gt;&lt;/item&gt;
  660. &lt;/data&gt;
  661. </example>
  662. </document>
  663. Fallback example (XInclude C.5)
  664. Note! Fallback support is not yet implemented
  665. >>> document = xinclude_loader("C5.xml")
  666. >>> ElementInclude.include(document, xinclude_loader)
  667. Traceback (most recent call last):
  668. IOError: resource not found
  669. >>> # print serialize(document) # C5
  670. """
  671. def xinclude_default():
  672. """
  673. >>> document = xinclude_loader("default.xml")
  674. >>> ElementInclude.include(document)
  675. >>> print serialize(document) # default
  676. <document>
  677. <p>Example.</p>
  678. <root>
  679. <element key="value">text</element>
  680. <element>text</element>tail
  681. <empty-element />
  682. </root>
  683. </document>
  684. """
  685. #
  686. # xmlwriter
  687. def xmlwriter():
  688. r"""
  689. >>> file = StringIO.StringIO()
  690. >>> w = SimpleXMLWriter.XMLWriter(file)
  691. >>> html = w.start("html")
  692. >>> x = w.start("head")
  693. >>> w.element("title", "my document")
  694. >>> w.data("\n")
  695. >>> w.element("meta", name="hello", value="goodbye")
  696. >>> w.data("\n")
  697. >>> w.end()
  698. >>> x = w.start("body")
  699. >>> w.element("h1", "this is a heading")
  700. >>> w.data("\n")
  701. >>> w.element("p", u"this is a paragraph")
  702. >>> w.data("\n")
  703. >>> w.element("p", u"reserved characters: <&>")
  704. >>> w.data("\n")
  705. >>> w.element("p", u"detta är också ett stycke")
  706. >>> w.data("\n")
  707. >>> w.close(html)
  708. >>> print file.getvalue()
  709. <html><head><title>my document</title>
  710. <meta name="hello" value="goodbye" />
  711. </head><body><h1>this is a heading</h1>
  712. <p>this is a paragraph</p>
  713. <p>reserved characters: &lt;&amp;&gt;</p>
  714. <p>detta &#228;r ocks&#229; ett stycke</p>
  715. </body></html>
  716. """
  717. # --------------------------------------------------------------------
  718. # reported bugs
  719. def bug_xmltoolkit21():
  720. """
  721. marshaller gives obscure errors for non-string values
  722. >>> elem = ElementTree.Element(123)
  723. >>> serialize(elem) # tag
  724. Traceback (most recent call last):
  725. TypeError: cannot serialize 123 (type int)
  726. >>> elem = ElementTree.Element("elem")
  727. >>> elem.text = 123
  728. >>> serialize(elem) # text
  729. Traceback (most recent call last):
  730. TypeError: cannot serialize 123 (type int)
  731. >>> elem = ElementTree.Element("elem")
  732. >>> elem.tail = 123
  733. >>> serialize(elem) # tail
  734. Traceback (most recent call last):
  735. TypeError: cannot serialize 123 (type int)
  736. >>> elem = ElementTree.Element("elem")
  737. >>> elem.set(123, "123")
  738. >>> serialize(elem) # attribute key
  739. Traceback (most recent call last):
  740. TypeError: cannot serialize 123 (type int)
  741. >>> elem = ElementTree.Element("elem")
  742. >>> elem.set("123", 123)
  743. >>> serialize(elem) # attribute value
  744. Traceback (most recent call last):
  745. TypeError: cannot serialize 123 (type int)
  746. """
  747. def bug_xmltoolkit25():
  748. """
  749. typo in ElementTree.findtext
  750. >>> tree = ElementTree.ElementTree(SAMPLE_XML)
  751. >>> tree.findtext("tag")
  752. 'text'
  753. >>> tree.findtext("section/tag")
  754. 'subtext'
  755. """
  756. def bug_xmltoolkit28():
  757. """
  758. .//tag causes exceptions
  759. >>> tree = ElementTree.XML("<doc><table><tbody/></table></doc>")
  760. >>> summarize_list(tree.findall(".//thead"))
  761. []
  762. >>> summarize_list(tree.findall(".//tbody"))
  763. ['tbody']
  764. """
  765. def bug_xmltoolkitX1():
  766. """
  767. dump() doesn't flush the output buffer
  768. >>> tree = ElementTree.XML("<doc><table><tbody/></table></doc>")
  769. >>> ElementTree.dump(tree); sys.stdout.write("tail")
  770. <doc><table><tbody /></table></doc>
  771. tail
  772. """
  773. def bug_xmltoolkit39():
  774. """
  775. non-ascii element and attribute names doesn't work
  776. >>> tree = ElementTree.XML("<?xml version='1.0' encoding='iso-8859-1'?><täg />")
  777. >>> ElementTree.tostring(tree, "utf-8")
  778. '<t\\xc3\\xa4g />'
  779. >>> tree = ElementTree.XML("<?xml version='1.0' encoding='iso-8859-1'?><tag ättr='v&#228;lue' />")
  780. >>> tree.attrib
  781. {u'\\xe4ttr': u'v\\xe4lue'}
  782. >>> ElementTree.tostring(tree, "utf-8")
  783. '<tag \\xc3\\xa4ttr="v\\xc3\\xa4lue" />'
  784. >>> tree = ElementTree.XML("<?xml version='1.0' encoding='iso-8859-1'?><täg>text</täg>")
  785. >>> ElementTree.tostring(tree, "utf-8")
  786. '<t\\xc3\\xa4g>text</t\\xc3\\xa4g>'
  787. >>> tree = ElementTree.Element(u"täg")
  788. >>> ElementTree.tostring(tree, "utf-8")
  789. '<t\\xc3\\xa4g />'
  790. >>> tree = ElementTree.Element("tag")
  791. >>> tree.set(u"ättr", u"välue")
  792. >>> ElementTree.tostring(tree, "utf-8")
  793. '<tag \\xc3\\xa4ttr="v\\xc3\\xa4lue" />'
  794. """
  795. def bug_xmltoolkit45():
  796. """
  797. problems parsing mixed unicode/non-ascii html documents
  798. latin-1 text
  799. >>> p = HTMLTreeBuilder.TreeBuilder()
  800. >>> p.feed("<p>välue</p>")
  801. >>> serialize(p.close())
  802. '<p>v&#228;lue</p>'
  803. utf-8 text
  804. >>> p = HTMLTreeBuilder.TreeBuilder(encoding="utf-8")
  805. >>> p.feed("<p>v\xc3\xa4lue</p>")
  806. >>> serialize(p.close())
  807. '<p>v&#228;lue</p>'
  808. utf-8 text using meta tag
  809. >>> p = HTMLTreeBuilder.TreeBuilder()
  810. >>> p.feed("<html><meta http-equiv='Content-Type' content='text/html; charset=utf-8'><p>v\xc3\xa4lue</p></html>")
  811. >>> serialize(p.close().find("p"))
  812. '<p>v&#228;lue</p>'
  813. latin-1 character references
  814. >>> p = HTMLTreeBuilder.TreeBuilder()
  815. >>> p.feed("<p>v&#228;lue</p>")
  816. >>> serialize(p.close())
  817. '<p>v&#228;lue</p>'
  818. latin-1 character entities
  819. >>> p = HTMLTreeBuilder.TreeBuilder()
  820. >>> p.feed("<p>v&auml;lue</p>")
  821. >>> serialize(p.close())
  822. '<p>v&#228;lue</p>'
  823. mixed latin-1 text and unicode entities
  824. >>> p = HTMLTreeBuilder.TreeBuilder()
  825. >>> p.feed("<p>&#8221;välue&#8221;</p>")
  826. >>> serialize(p.close())
  827. '<p>&#8221;v&#228;lue&#8221;</p>'
  828. mixed unicode and latin-1 entities
  829. >>> p = HTMLTreeBuilder.TreeBuilder()
  830. >>> p.feed("<p>&#8221;v&auml;lue&#8221;</p>")
  831. >>> serialize(p.close())
  832. '<p>&#8221;v&#228;lue&#8221;</p>'
  833. """
  834. def bug_xmltoolkit46():
  835. """
  836. problems parsing open BR tags
  837. >>> p = HTMLTreeBuilder.TreeBuilder()
  838. >>> p.feed("<p>key<br>value</p>")
  839. >>> serialize(p.close())
  840. '<p>key<br />value</p>'
  841. """
  842. def bug_xmltoolkit54():
  843. """
  844. problems handling internally defined entities
  845. >>> e = ElementTree.XML("<!DOCTYPE doc [<!ENTITY ldots '&#x8230;'>]><doc>&ldots;</doc>")
  846. >>> serialize(e)
  847. '<doc>&#33328;</doc>'
  848. """
  849. def bug_xmltoolkit55():
  850. """
  851. make sure we're reporting the first error, not the last
  852. >>> e = ElementTree.XML("<!DOCTYPE doc SYSTEM 'doc.dtd'><doc>&ldots;&ndots;&rdots;</doc>")
  853. Traceback (most recent call last):
  854. ExpatError: undefined entity &ldots;: line 1, column 36
  855. """
  856. # --------------------------------------------------------------------
  857. if __name__ == "__main__":
  858. import doctest, selftest
  859. failed, tested = doctest.testmod(selftest)
  860. print tested - failed, "tests ok."