test_minidom.py 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452
  1. # test for xml.dom.minidom
  2. import copy
  3. import pickle
  4. from StringIO import StringIO
  5. from test.test_support import verbose, run_unittest, findfile
  6. import unittest
  7. import xml.dom
  8. import xml.dom.minidom
  9. import xml.parsers.expat
  10. from xml.dom.minidom import parse, Node, Document, parseString
  11. from xml.dom.minidom import getDOMImplementation
  12. tstfile = findfile("test.xml", subdir="xmltestdata")
  13. sample = ("<?xml version='1.0' encoding='us-ascii'?>\n"
  14. "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'"
  15. " 'http://xml.python.org/system' [\n"
  16. " <!ELEMENT e EMPTY>\n"
  17. " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n"
  18. "]><doc attr='value'> text\n"
  19. "<?pi sample?> <!-- comment --> <e/> </doc>")
  20. # The tests of DocumentType importing use these helpers to construct
  21. # the documents to work with, since not all DOM builders actually
  22. # create the DocumentType nodes.
  23. def create_doc_without_doctype(doctype=None):
  24. return getDOMImplementation().createDocument(None, "doc", doctype)
  25. def create_nonempty_doctype():
  26. doctype = getDOMImplementation().createDocumentType("doc", None, None)
  27. doctype.entities._seq = []
  28. doctype.notations._seq = []
  29. notation = xml.dom.minidom.Notation("my-notation", None,
  30. "http://xml.python.org/notations/my")
  31. doctype.notations._seq.append(notation)
  32. entity = xml.dom.minidom.Entity("my-entity", None,
  33. "http://xml.python.org/entities/my",
  34. "my-notation")
  35. entity.version = "1.0"
  36. entity.encoding = "utf-8"
  37. entity.actualEncoding = "us-ascii"
  38. doctype.entities._seq.append(entity)
  39. return doctype
  40. def create_doc_with_doctype():
  41. doctype = create_nonempty_doctype()
  42. doc = create_doc_without_doctype(doctype)
  43. doctype.entities.item(0).ownerDocument = doc
  44. doctype.notations.item(0).ownerDocument = doc
  45. return doc
  46. class MinidomTest(unittest.TestCase):
  47. def confirm(self, test, testname = "Test"):
  48. self.assertTrue(test, testname)
  49. def checkWholeText(self, node, s):
  50. t = node.wholeText
  51. self.confirm(t == s, "looking for %s, found %s" % (repr(s), repr(t)))
  52. def testParseFromFile(self):
  53. dom = parse(StringIO(open(tstfile).read()))
  54. dom.unlink()
  55. self.confirm(isinstance(dom,Document))
  56. def testGetElementsByTagName(self):
  57. dom = parse(tstfile)
  58. self.confirm(dom.getElementsByTagName("LI") == \
  59. dom.documentElement.getElementsByTagName("LI"))
  60. dom.unlink()
  61. def testInsertBefore(self):
  62. dom = parseString("<doc><foo/></doc>")
  63. root = dom.documentElement
  64. elem = root.childNodes[0]
  65. nelem = dom.createElement("element")
  66. root.insertBefore(nelem, elem)
  67. self.confirm(len(root.childNodes) == 2
  68. and root.childNodes.length == 2
  69. and root.childNodes[0] is nelem
  70. and root.childNodes.item(0) is nelem
  71. and root.childNodes[1] is elem
  72. and root.childNodes.item(1) is elem
  73. and root.firstChild is nelem
  74. and root.lastChild is elem
  75. and root.toxml() == "<doc><element/><foo/></doc>"
  76. , "testInsertBefore -- node properly placed in tree")
  77. nelem = dom.createElement("element")
  78. root.insertBefore(nelem, None)
  79. self.confirm(len(root.childNodes) == 3
  80. and root.childNodes.length == 3
  81. and root.childNodes[1] is elem
  82. and root.childNodes.item(1) is elem
  83. and root.childNodes[2] is nelem
  84. and root.childNodes.item(2) is nelem
  85. and root.lastChild is nelem
  86. and nelem.previousSibling is elem
  87. and root.toxml() == "<doc><element/><foo/><element/></doc>"
  88. , "testInsertBefore -- node properly placed in tree")
  89. nelem2 = dom.createElement("bar")
  90. root.insertBefore(nelem2, nelem)
  91. self.confirm(len(root.childNodes) == 4
  92. and root.childNodes.length == 4
  93. and root.childNodes[2] is nelem2
  94. and root.childNodes.item(2) is nelem2
  95. and root.childNodes[3] is nelem
  96. and root.childNodes.item(3) is nelem
  97. and nelem2.nextSibling is nelem
  98. and nelem.previousSibling is nelem2
  99. and root.toxml() ==
  100. "<doc><element/><foo/><bar/><element/></doc>"
  101. , "testInsertBefore -- node properly placed in tree")
  102. dom.unlink()
  103. def _create_fragment_test_nodes(self):
  104. dom = parseString("<doc/>")
  105. orig = dom.createTextNode("original")
  106. c1 = dom.createTextNode("foo")
  107. c2 = dom.createTextNode("bar")
  108. c3 = dom.createTextNode("bat")
  109. dom.documentElement.appendChild(orig)
  110. frag = dom.createDocumentFragment()
  111. frag.appendChild(c1)
  112. frag.appendChild(c2)
  113. frag.appendChild(c3)
  114. return dom, orig, c1, c2, c3, frag
  115. def testInsertBeforeFragment(self):
  116. dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
  117. dom.documentElement.insertBefore(frag, None)
  118. self.confirm(tuple(dom.documentElement.childNodes) ==
  119. (orig, c1, c2, c3),
  120. "insertBefore(<fragment>, None)")
  121. frag.unlink()
  122. dom.unlink()
  123. dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
  124. dom.documentElement.insertBefore(frag, orig)
  125. self.confirm(tuple(dom.documentElement.childNodes) ==
  126. (c1, c2, c3, orig),
  127. "insertBefore(<fragment>, orig)")
  128. frag.unlink()
  129. dom.unlink()
  130. def testAppendChild(self):
  131. dom = parse(tstfile)
  132. dom.documentElement.appendChild(dom.createComment(u"Hello"))
  133. self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment")
  134. self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
  135. dom.unlink()
  136. def testAppendChildFragment(self):
  137. dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
  138. dom.documentElement.appendChild(frag)
  139. self.confirm(tuple(dom.documentElement.childNodes) ==
  140. (orig, c1, c2, c3),
  141. "appendChild(<fragment>)")
  142. frag.unlink()
  143. dom.unlink()
  144. def testReplaceChildFragment(self):
  145. dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
  146. dom.documentElement.replaceChild(frag, orig)
  147. orig.unlink()
  148. self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3),
  149. "replaceChild(<fragment>)")
  150. frag.unlink()
  151. dom.unlink()
  152. def testLegalChildren(self):
  153. dom = Document()
  154. elem = dom.createElement('element')
  155. text = dom.createTextNode('text')
  156. self.assertRaises(xml.dom.HierarchyRequestErr, dom.appendChild, text)
  157. dom.appendChild(elem)
  158. self.assertRaises(xml.dom.HierarchyRequestErr, dom.insertBefore, text,
  159. elem)
  160. self.assertRaises(xml.dom.HierarchyRequestErr, dom.replaceChild, text,
  161. elem)
  162. nodemap = elem.attributes
  163. self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItem,
  164. text)
  165. self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItemNS,
  166. text)
  167. elem.appendChild(text)
  168. dom.unlink()
  169. def testNamedNodeMapSetItem(self):
  170. dom = Document()
  171. elem = dom.createElement('element')
  172. attrs = elem.attributes
  173. attrs["foo"] = "bar"
  174. a = attrs.item(0)
  175. self.confirm(a.ownerDocument is dom,
  176. "NamedNodeMap.__setitem__() sets ownerDocument")
  177. self.confirm(a.ownerElement is elem,
  178. "NamedNodeMap.__setitem__() sets ownerElement")
  179. self.confirm(a.value == "bar",
  180. "NamedNodeMap.__setitem__() sets value")
  181. self.confirm(a.nodeValue == "bar",
  182. "NamedNodeMap.__setitem__() sets nodeValue")
  183. elem.unlink()
  184. dom.unlink()
  185. def testNonZero(self):
  186. dom = parse(tstfile)
  187. self.confirm(dom)# should not be zero
  188. dom.appendChild(dom.createComment("foo"))
  189. self.confirm(not dom.childNodes[-1].childNodes)
  190. dom.unlink()
  191. def testUnlink(self):
  192. dom = parse(tstfile)
  193. dom.unlink()
  194. def testElement(self):
  195. dom = Document()
  196. dom.appendChild(dom.createElement("abc"))
  197. self.confirm(dom.documentElement)
  198. dom.unlink()
  199. def testAAA(self):
  200. dom = parseString("<abc/>")
  201. el = dom.documentElement
  202. el.setAttribute("spam", "jam2")
  203. self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAA")
  204. a = el.getAttributeNode("spam")
  205. self.confirm(a.ownerDocument is dom,
  206. "setAttribute() sets ownerDocument")
  207. self.confirm(a.ownerElement is dom.documentElement,
  208. "setAttribute() sets ownerElement")
  209. dom.unlink()
  210. def testAAB(self):
  211. dom = parseString("<abc/>")
  212. el = dom.documentElement
  213. el.setAttribute("spam", "jam")
  214. el.setAttribute("spam", "jam2")
  215. self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAB")
  216. dom.unlink()
  217. def testAddAttr(self):
  218. dom = Document()
  219. child = dom.appendChild(dom.createElement("abc"))
  220. child.setAttribute("def", "ghi")
  221. self.confirm(child.getAttribute("def") == "ghi")
  222. self.confirm(child.attributes["def"].value == "ghi")
  223. child.setAttribute("jkl", "mno")
  224. self.confirm(child.getAttribute("jkl") == "mno")
  225. self.confirm(child.attributes["jkl"].value == "mno")
  226. self.confirm(len(child.attributes) == 2)
  227. child.setAttribute("def", "newval")
  228. self.confirm(child.getAttribute("def") == "newval")
  229. self.confirm(child.attributes["def"].value == "newval")
  230. self.confirm(len(child.attributes) == 2)
  231. dom.unlink()
  232. def testDeleteAttr(self):
  233. dom = Document()
  234. child = dom.appendChild(dom.createElement("abc"))
  235. self.confirm(len(child.attributes) == 0)
  236. child.setAttribute("def", "ghi")
  237. self.confirm(len(child.attributes) == 1)
  238. del child.attributes["def"]
  239. self.confirm(len(child.attributes) == 0)
  240. dom.unlink()
  241. def testRemoveAttr(self):
  242. dom = Document()
  243. child = dom.appendChild(dom.createElement("abc"))
  244. child.setAttribute("def", "ghi")
  245. self.confirm(len(child.attributes) == 1)
  246. child.removeAttribute("def")
  247. self.confirm(len(child.attributes) == 0)
  248. dom.unlink()
  249. def testRemoveAttrNS(self):
  250. dom = Document()
  251. child = dom.appendChild(
  252. dom.createElementNS("http://www.python.org", "python:abc"))
  253. child.setAttributeNS("http://www.w3.org", "xmlns:python",
  254. "http://www.python.org")
  255. child.setAttributeNS("http://www.python.org", "python:abcattr", "foo")
  256. self.confirm(len(child.attributes) == 2)
  257. child.removeAttributeNS("http://www.python.org", "abcattr")
  258. self.confirm(len(child.attributes) == 1)
  259. dom.unlink()
  260. def testRemoveAttributeNode(self):
  261. dom = Document()
  262. child = dom.appendChild(dom.createElement("foo"))
  263. child.setAttribute("spam", "jam")
  264. self.confirm(len(child.attributes) == 1)
  265. node = child.getAttributeNode("spam")
  266. child.removeAttributeNode(node)
  267. self.confirm(len(child.attributes) == 0
  268. and child.getAttributeNode("spam") is None)
  269. dom.unlink()
  270. def testChangeAttr(self):
  271. dom = parseString("<abc/>")
  272. el = dom.documentElement
  273. el.setAttribute("spam", "jam")
  274. self.confirm(len(el.attributes) == 1)
  275. el.setAttribute("spam", "bam")
  276. # Set this attribute to be an ID and make sure that doesn't change
  277. # when changing the value:
  278. el.setIdAttribute("spam")
  279. self.confirm(len(el.attributes) == 1
  280. and el.attributes["spam"].value == "bam"
  281. and el.attributes["spam"].nodeValue == "bam"
  282. and el.getAttribute("spam") == "bam"
  283. and el.getAttributeNode("spam").isId)
  284. el.attributes["spam"] = "ham"
  285. self.confirm(len(el.attributes) == 1
  286. and el.attributes["spam"].value == "ham"
  287. and el.attributes["spam"].nodeValue == "ham"
  288. and el.getAttribute("spam") == "ham"
  289. and el.attributes["spam"].isId)
  290. el.setAttribute("spam2", "bam")
  291. self.confirm(len(el.attributes) == 2
  292. and el.attributes["spam"].value == "ham"
  293. and el.attributes["spam"].nodeValue == "ham"
  294. and el.getAttribute("spam") == "ham"
  295. and el.attributes["spam2"].value == "bam"
  296. and el.attributes["spam2"].nodeValue == "bam"
  297. and el.getAttribute("spam2") == "bam")
  298. el.attributes["spam2"] = "bam2"
  299. self.confirm(len(el.attributes) == 2
  300. and el.attributes["spam"].value == "ham"
  301. and el.attributes["spam"].nodeValue == "ham"
  302. and el.getAttribute("spam") == "ham"
  303. and el.attributes["spam2"].value == "bam2"
  304. and el.attributes["spam2"].nodeValue == "bam2"
  305. and el.getAttribute("spam2") == "bam2")
  306. dom.unlink()
  307. def testGetElementsByTagNameNS(self):
  308. d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'>
  309. <minidom:myelem/>
  310. </foo>"""
  311. dom = parseString(d)
  312. elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom",
  313. "myelem")
  314. self.confirm(len(elems) == 1
  315. and elems[0].namespaceURI == "http://pyxml.sf.net/minidom"
  316. and elems[0].localName == "myelem"
  317. and elems[0].prefix == "minidom"
  318. and elems[0].tagName == "minidom:myelem"
  319. and elems[0].nodeName == "minidom:myelem")
  320. dom.unlink()
  321. def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri,
  322. lname):
  323. nodelist = doc.getElementsByTagNameNS(nsuri, lname)
  324. self.confirm(len(nodelist) == 0)
  325. def testGetEmptyNodeListFromElementsByTagNameNS(self):
  326. doc = parseString('<doc/>')
  327. self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
  328. doc, 'http://xml.python.org/namespaces/a', 'localname')
  329. self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
  330. doc, '*', 'splat')
  331. self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
  332. doc, 'http://xml.python.org/namespaces/a', '*')
  333. doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>')
  334. self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
  335. doc, "http://xml.python.org/splat", "not-there")
  336. self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
  337. doc, "*", "not-there")
  338. self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
  339. doc, "http://somewhere.else.net/not-there", "e")
  340. def testElementReprAndStr(self):
  341. dom = Document()
  342. el = dom.appendChild(dom.createElement("abc"))
  343. string1 = repr(el)
  344. string2 = str(el)
  345. self.confirm(string1 == string2)
  346. dom.unlink()
  347. def testElementReprAndStrUnicode(self):
  348. dom = Document()
  349. el = dom.appendChild(dom.createElement(u"abc"))
  350. string1 = repr(el)
  351. string2 = str(el)
  352. self.confirm(string1 == string2)
  353. dom.unlink()
  354. def testElementReprAndStrUnicodeNS(self):
  355. dom = Document()
  356. el = dom.appendChild(
  357. dom.createElementNS(u"http://www.slashdot.org", u"slash:abc"))
  358. string1 = repr(el)
  359. string2 = str(el)
  360. self.confirm(string1 == string2)
  361. self.confirm("slash:abc" in string1)
  362. dom.unlink()
  363. def testAttributeRepr(self):
  364. dom = Document()
  365. el = dom.appendChild(dom.createElement(u"abc"))
  366. node = el.setAttribute("abc", "def")
  367. self.confirm(str(node) == repr(node))
  368. dom.unlink()
  369. def testWriteXML(self):
  370. str = '<?xml version="1.0" ?><a b="c"/>'
  371. dom = parseString(str)
  372. domstr = dom.toxml()
  373. dom.unlink()
  374. self.confirm(str == domstr)
  375. def testAltNewline(self):
  376. str = '<?xml version="1.0" ?>\n<a b="c"/>\n'
  377. dom = parseString(str)
  378. domstr = dom.toprettyxml(newl="\r\n")
  379. dom.unlink()
  380. self.confirm(domstr == str.replace("\n", "\r\n"))
  381. def test_toprettyxml_with_text_nodes(self):
  382. # see issue #4147, text nodes are not indented
  383. decl = '<?xml version="1.0" ?>\n'
  384. self.assertEqual(parseString('<B>A</B>').toprettyxml(),
  385. decl + '<B>A</B>\n')
  386. self.assertEqual(parseString('<C>A<B>A</B></C>').toprettyxml(),
  387. decl + '<C>\n\tA\n\t<B>A</B>\n</C>\n')
  388. self.assertEqual(parseString('<C><B>A</B>A</C>').toprettyxml(),
  389. decl + '<C>\n\t<B>A</B>\n\tA\n</C>\n')
  390. self.assertEqual(parseString('<C><B>A</B><B>A</B></C>').toprettyxml(),
  391. decl + '<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n')
  392. self.assertEqual(parseString('<C><B>A</B>A<B>A</B></C>').toprettyxml(),
  393. decl + '<C>\n\t<B>A</B>\n\tA\n\t<B>A</B>\n</C>\n')
  394. def test_toprettyxml_with_adjacent_text_nodes(self):
  395. # see issue #4147, adjacent text nodes are indented normally
  396. dom = Document()
  397. elem = dom.createElement(u'elem')
  398. elem.appendChild(dom.createTextNode(u'TEXT'))
  399. elem.appendChild(dom.createTextNode(u'TEXT'))
  400. dom.appendChild(elem)
  401. decl = '<?xml version="1.0" ?>\n'
  402. self.assertEqual(dom.toprettyxml(),
  403. decl + '<elem>\n\tTEXT\n\tTEXT\n</elem>\n')
  404. def test_toprettyxml_preserves_content_of_text_node(self):
  405. # see issue #4147
  406. for str in ('<B>A</B>', '<A><B>C</B></A>'):
  407. dom = parseString(str)
  408. dom2 = parseString(dom.toprettyxml())
  409. self.assertEqual(
  410. dom.getElementsByTagName('B')[0].childNodes[0].toxml(),
  411. dom2.getElementsByTagName('B')[0].childNodes[0].toxml())
  412. def testProcessingInstruction(self):
  413. dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
  414. pi = dom.documentElement.firstChild
  415. self.confirm(pi.target == "mypi"
  416. and pi.data == "data \t\n "
  417. and pi.nodeName == "mypi"
  418. and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE
  419. and pi.attributes is None
  420. and not pi.hasChildNodes()
  421. and len(pi.childNodes) == 0
  422. and pi.firstChild is None
  423. and pi.lastChild is None
  424. and pi.localName is None
  425. and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE)
  426. def testTooManyDocumentElements(self):
  427. doc = parseString("<doc/>")
  428. elem = doc.createElement("extra")
  429. # Should raise an exception when adding an extra document element.
  430. self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem)
  431. elem.unlink()
  432. doc.unlink()
  433. def testRemoveNamedItem(self):
  434. doc = parseString("<doc a=''/>")
  435. e = doc.documentElement
  436. attrs = e.attributes
  437. a1 = e.getAttributeNode("a")
  438. a2 = attrs.removeNamedItem("a")
  439. self.confirm(a1.isSameNode(a2))
  440. self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a")
  441. def testRemoveNamedItemNS(self):
  442. doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>")
  443. e = doc.documentElement
  444. attrs = e.attributes
  445. a1 = e.getAttributeNodeNS("http://xml.python.org/", "b")
  446. a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b")
  447. self.confirm(a1.isSameNode(a2))
  448. self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS,
  449. "http://xml.python.org/", "b")
  450. def _testCloneElementCopiesAttributes(self, e1, e2, test):
  451. attrs1 = e1.attributes
  452. attrs2 = e2.attributes
  453. keys1 = attrs1.keys()
  454. keys2 = attrs2.keys()
  455. keys1.sort()
  456. keys2.sort()
  457. self.confirm(keys1 == keys2, "clone of element has same attribute keys")
  458. for i in range(len(keys1)):
  459. a1 = attrs1.item(i)
  460. a2 = attrs2.item(i)
  461. self.confirm(a1 is not a2
  462. and a1.value == a2.value
  463. and a1.nodeValue == a2.nodeValue
  464. and a1.namespaceURI == a2.namespaceURI
  465. and a1.localName == a2.localName
  466. , "clone of attribute node has proper attribute values")
  467. self.confirm(a2.ownerElement is e2,
  468. "clone of attribute node correctly owned")
  469. def _setupCloneElement(self, deep):
  470. dom = parseString("<doc attr='value'><foo/></doc>")
  471. root = dom.documentElement
  472. clone = root.cloneNode(deep)
  473. self._testCloneElementCopiesAttributes(
  474. root, clone, "testCloneElement" + (deep and "Deep" or "Shallow"))
  475. # mutilate the original so shared data is detected
  476. root.tagName = root.nodeName = "MODIFIED"
  477. root.setAttribute("attr", "NEW VALUE")
  478. root.setAttribute("added", "VALUE")
  479. return dom, clone
  480. def testCloneElementShallow(self):
  481. dom, clone = self._setupCloneElement(0)
  482. self.confirm(len(clone.childNodes) == 0
  483. and clone.childNodes.length == 0
  484. and clone.parentNode is None
  485. and clone.toxml() == '<doc attr="value"/>'
  486. , "testCloneElementShallow")
  487. dom.unlink()
  488. def testCloneElementDeep(self):
  489. dom, clone = self._setupCloneElement(1)
  490. self.confirm(len(clone.childNodes) == 1
  491. and clone.childNodes.length == 1
  492. and clone.parentNode is None
  493. and clone.toxml() == '<doc attr="value"><foo/></doc>'
  494. , "testCloneElementDeep")
  495. dom.unlink()
  496. def testCloneDocumentShallow(self):
  497. doc = parseString("<?xml version='1.0'?>\n"
  498. "<!-- comment -->"
  499. "<!DOCTYPE doc [\n"
  500. "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
  501. "]>\n"
  502. "<doc attr='value'/>")
  503. doc2 = doc.cloneNode(0)
  504. self.confirm(doc2 is None,
  505. "testCloneDocumentShallow:"
  506. " shallow cloning of documents makes no sense!")
  507. def testCloneDocumentDeep(self):
  508. doc = parseString("<?xml version='1.0'?>\n"
  509. "<!-- comment -->"
  510. "<!DOCTYPE doc [\n"
  511. "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
  512. "]>\n"
  513. "<doc attr='value'/>")
  514. doc2 = doc.cloneNode(1)
  515. self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)),
  516. "testCloneDocumentDeep: document objects not distinct")
  517. self.confirm(len(doc.childNodes) == len(doc2.childNodes),
  518. "testCloneDocumentDeep: wrong number of Document children")
  519. self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE,
  520. "testCloneDocumentDeep: documentElement not an ELEMENT_NODE")
  521. self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2),
  522. "testCloneDocumentDeep: documentElement owner is not new document")
  523. self.confirm(not doc.documentElement.isSameNode(doc2.documentElement),
  524. "testCloneDocumentDeep: documentElement should not be shared")
  525. if doc.doctype is not None:
  526. # check the doctype iff the original DOM maintained it
  527. self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE,
  528. "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE")
  529. self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2))
  530. self.confirm(not doc.doctype.isSameNode(doc2.doctype))
  531. def testCloneDocumentTypeDeepOk(self):
  532. doctype = create_nonempty_doctype()
  533. clone = doctype.cloneNode(1)
  534. self.confirm(clone is not None
  535. and clone.nodeName == doctype.nodeName
  536. and clone.name == doctype.name
  537. and clone.publicId == doctype.publicId
  538. and clone.systemId == doctype.systemId
  539. and len(clone.entities) == len(doctype.entities)
  540. and clone.entities.item(len(clone.entities)) is None
  541. and len(clone.notations) == len(doctype.notations)
  542. and clone.notations.item(len(clone.notations)) is None
  543. and len(clone.childNodes) == 0)
  544. for i in range(len(doctype.entities)):
  545. se = doctype.entities.item(i)
  546. ce = clone.entities.item(i)
  547. self.confirm((not se.isSameNode(ce))
  548. and (not ce.isSameNode(se))
  549. and ce.nodeName == se.nodeName
  550. and ce.notationName == se.notationName
  551. and ce.publicId == se.publicId
  552. and ce.systemId == se.systemId
  553. and ce.encoding == se.encoding
  554. and ce.actualEncoding == se.actualEncoding
  555. and ce.version == se.version)
  556. for i in range(len(doctype.notations)):
  557. sn = doctype.notations.item(i)
  558. cn = clone.notations.item(i)
  559. self.confirm((not sn.isSameNode(cn))
  560. and (not cn.isSameNode(sn))
  561. and cn.nodeName == sn.nodeName
  562. and cn.publicId == sn.publicId
  563. and cn.systemId == sn.systemId)
  564. def testCloneDocumentTypeDeepNotOk(self):
  565. doc = create_doc_with_doctype()
  566. clone = doc.doctype.cloneNode(1)
  567. self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk")
  568. def testCloneDocumentTypeShallowOk(self):
  569. doctype = create_nonempty_doctype()
  570. clone = doctype.cloneNode(0)
  571. self.confirm(clone is not None
  572. and clone.nodeName == doctype.nodeName
  573. and clone.name == doctype.name
  574. and clone.publicId == doctype.publicId
  575. and clone.systemId == doctype.systemId
  576. and len(clone.entities) == 0
  577. and clone.entities.item(0) is None
  578. and len(clone.notations) == 0
  579. and clone.notations.item(0) is None
  580. and len(clone.childNodes) == 0)
  581. def testCloneDocumentTypeShallowNotOk(self):
  582. doc = create_doc_with_doctype()
  583. clone = doc.doctype.cloneNode(0)
  584. self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk")
  585. def check_import_document(self, deep, testName):
  586. doc1 = parseString("<doc/>")
  587. doc2 = parseString("<doc/>")
  588. self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep)
  589. def testImportDocumentShallow(self):
  590. self.check_import_document(0, "testImportDocumentShallow")
  591. def testImportDocumentDeep(self):
  592. self.check_import_document(1, "testImportDocumentDeep")
  593. def testImportDocumentTypeShallow(self):
  594. src = create_doc_with_doctype()
  595. target = create_doc_without_doctype()
  596. self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
  597. src.doctype, 0)
  598. def testImportDocumentTypeDeep(self):
  599. src = create_doc_with_doctype()
  600. target = create_doc_without_doctype()
  601. self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
  602. src.doctype, 1)
  603. # Testing attribute clones uses a helper, and should always be deep,
  604. # even if the argument to cloneNode is false.
  605. def check_clone_attribute(self, deep, testName):
  606. doc = parseString("<doc attr='value'/>")
  607. attr = doc.documentElement.getAttributeNode("attr")
  608. self.assertNotEqual(attr, None)
  609. clone = attr.cloneNode(deep)
  610. self.confirm(not clone.isSameNode(attr))
  611. self.confirm(not attr.isSameNode(clone))
  612. self.confirm(clone.ownerElement is None,
  613. testName + ": ownerElement should be None")
  614. self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument),
  615. testName + ": ownerDocument does not match")
  616. self.confirm(clone.specified,
  617. testName + ": cloned attribute must have specified == True")
  618. def testCloneAttributeShallow(self):
  619. self.check_clone_attribute(0, "testCloneAttributeShallow")
  620. def testCloneAttributeDeep(self):
  621. self.check_clone_attribute(1, "testCloneAttributeDeep")
  622. def check_clone_pi(self, deep, testName):
  623. doc = parseString("<?target data?><doc/>")
  624. pi = doc.firstChild
  625. self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE)
  626. clone = pi.cloneNode(deep)
  627. self.confirm(clone.target == pi.target
  628. and clone.data == pi.data)
  629. def testClonePIShallow(self):
  630. self.check_clone_pi(0, "testClonePIShallow")
  631. def testClonePIDeep(self):
  632. self.check_clone_pi(1, "testClonePIDeep")
  633. def testNormalize(self):
  634. doc = parseString("<doc/>")
  635. root = doc.documentElement
  636. root.appendChild(doc.createTextNode("first"))
  637. root.appendChild(doc.createTextNode("second"))
  638. self.confirm(len(root.childNodes) == 2
  639. and root.childNodes.length == 2,
  640. "testNormalize -- preparation")
  641. doc.normalize()
  642. self.confirm(len(root.childNodes) == 1
  643. and root.childNodes.length == 1
  644. and root.firstChild is root.lastChild
  645. and root.firstChild.data == "firstsecond"
  646. , "testNormalize -- result")
  647. doc.unlink()
  648. doc = parseString("<doc/>")
  649. root = doc.documentElement
  650. root.appendChild(doc.createTextNode(""))
  651. doc.normalize()
  652. self.confirm(len(root.childNodes) == 0
  653. and root.childNodes.length == 0,
  654. "testNormalize -- single empty node removed")
  655. doc.unlink()
  656. def testNormalizeCombineAndNextSibling(self):
  657. doc = parseString("<doc/>")
  658. root = doc.documentElement
  659. root.appendChild(doc.createTextNode("first"))
  660. root.appendChild(doc.createTextNode("second"))
  661. root.appendChild(doc.createElement("i"))
  662. self.confirm(len(root.childNodes) == 3
  663. and root.childNodes.length == 3,
  664. "testNormalizeCombineAndNextSibling -- preparation")
  665. doc.normalize()
  666. self.confirm(len(root.childNodes) == 2
  667. and root.childNodes.length == 2
  668. and root.firstChild.data == "firstsecond"
  669. and root.firstChild is not root.lastChild
  670. and root.firstChild.nextSibling is root.lastChild
  671. and root.firstChild.previousSibling is None
  672. and root.lastChild.previousSibling is root.firstChild
  673. and root.lastChild.nextSibling is None
  674. , "testNormalizeCombinedAndNextSibling -- result")
  675. doc.unlink()
  676. def testNormalizeDeleteWithPrevSibling(self):
  677. doc = parseString("<doc/>")
  678. root = doc.documentElement
  679. root.appendChild(doc.createTextNode("first"))
  680. root.appendChild(doc.createTextNode(""))
  681. self.confirm(len(root.childNodes) == 2
  682. and root.childNodes.length == 2,
  683. "testNormalizeDeleteWithPrevSibling -- preparation")
  684. doc.normalize()
  685. self.confirm(len(root.childNodes) == 1
  686. and root.childNodes.length == 1
  687. and root.firstChild.data == "first"
  688. and root.firstChild is root.lastChild
  689. and root.firstChild.nextSibling is None
  690. and root.firstChild.previousSibling is None
  691. , "testNormalizeDeleteWithPrevSibling -- result")
  692. doc.unlink()
  693. def testNormalizeDeleteWithNextSibling(self):
  694. doc = parseString("<doc/>")
  695. root = doc.documentElement
  696. root.appendChild(doc.createTextNode(""))
  697. root.appendChild(doc.createTextNode("second"))
  698. self.confirm(len(root.childNodes) == 2
  699. and root.childNodes.length == 2,
  700. "testNormalizeDeleteWithNextSibling -- preparation")
  701. doc.normalize()
  702. self.confirm(len(root.childNodes) == 1
  703. and root.childNodes.length == 1
  704. and root.firstChild.data == "second"
  705. and root.firstChild is root.lastChild
  706. and root.firstChild.nextSibling is None
  707. and root.firstChild.previousSibling is None
  708. , "testNormalizeDeleteWithNextSibling -- result")
  709. doc.unlink()
  710. def testNormalizeDeleteWithTwoNonTextSiblings(self):
  711. doc = parseString("<doc/>")
  712. root = doc.documentElement
  713. root.appendChild(doc.createElement("i"))
  714. root.appendChild(doc.createTextNode(""))
  715. root.appendChild(doc.createElement("i"))
  716. self.confirm(len(root.childNodes) == 3
  717. and root.childNodes.length == 3,
  718. "testNormalizeDeleteWithTwoSiblings -- preparation")
  719. doc.normalize()
  720. self.confirm(len(root.childNodes) == 2
  721. and root.childNodes.length == 2
  722. and root.firstChild is not root.lastChild
  723. and root.firstChild.nextSibling is root.lastChild
  724. and root.firstChild.previousSibling is None
  725. and root.lastChild.previousSibling is root.firstChild
  726. and root.lastChild.nextSibling is None
  727. , "testNormalizeDeleteWithTwoSiblings -- result")
  728. doc.unlink()
  729. def testNormalizeDeleteAndCombine(self):
  730. doc = parseString("<doc/>")
  731. root = doc.documentElement
  732. root.appendChild(doc.createTextNode(""))
  733. root.appendChild(doc.createTextNode("second"))
  734. root.appendChild(doc.createTextNode(""))
  735. root.appendChild(doc.createTextNode("fourth"))
  736. root.appendChild(doc.createTextNode(""))
  737. self.confirm(len(root.childNodes) == 5
  738. and root.childNodes.length == 5,
  739. "testNormalizeDeleteAndCombine -- preparation")
  740. doc.normalize()
  741. self.confirm(len(root.childNodes) == 1
  742. and root.childNodes.length == 1
  743. and root.firstChild is root.lastChild
  744. and root.firstChild.data == "secondfourth"
  745. and root.firstChild.previousSibling is None
  746. and root.firstChild.nextSibling is None
  747. , "testNormalizeDeleteAndCombine -- result")
  748. doc.unlink()
  749. def testNormalizeRecursion(self):
  750. doc = parseString("<doc>"
  751. "<o>"
  752. "<i/>"
  753. "t"
  754. #
  755. #x
  756. "</o>"
  757. "<o>"
  758. "<o>"
  759. "t2"
  760. #x2
  761. "</o>"
  762. "t3"
  763. #x3
  764. "</o>"
  765. #
  766. "</doc>")
  767. root = doc.documentElement
  768. root.childNodes[0].appendChild(doc.createTextNode(""))
  769. root.childNodes[0].appendChild(doc.createTextNode("x"))
  770. root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
  771. root.childNodes[1].appendChild(doc.createTextNode("x3"))
  772. root.appendChild(doc.createTextNode(""))
  773. self.confirm(len(root.childNodes) == 3
  774. and root.childNodes.length == 3
  775. and len(root.childNodes[0].childNodes) == 4
  776. and root.childNodes[0].childNodes.length == 4
  777. and len(root.childNodes[1].childNodes) == 3
  778. and root.childNodes[1].childNodes.length == 3
  779. and len(root.childNodes[1].childNodes[0].childNodes) == 2
  780. and root.childNodes[1].childNodes[0].childNodes.length == 2
  781. , "testNormalize2 -- preparation")
  782. doc.normalize()
  783. self.confirm(len(root.childNodes) == 2
  784. and root.childNodes.length == 2
  785. and len(root.childNodes[0].childNodes) == 2
  786. and root.childNodes[0].childNodes.length == 2
  787. and len(root.childNodes[1].childNodes) == 2
  788. and root.childNodes[1].childNodes.length == 2
  789. and len(root.childNodes[1].childNodes[0].childNodes) == 1
  790. and root.childNodes[1].childNodes[0].childNodes.length == 1
  791. , "testNormalize2 -- childNodes lengths")
  792. self.confirm(root.childNodes[0].childNodes[1].data == "tx"
  793. and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
  794. and root.childNodes[1].childNodes[1].data == "t3x3"
  795. , "testNormalize2 -- joined text fields")
  796. self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
  797. and root.childNodes[0].childNodes[1].previousSibling
  798. is root.childNodes[0].childNodes[0]
  799. and root.childNodes[0].childNodes[0].previousSibling is None
  800. and root.childNodes[0].childNodes[0].nextSibling
  801. is root.childNodes[0].childNodes[1]
  802. and root.childNodes[1].childNodes[1].nextSibling is None
  803. and root.childNodes[1].childNodes[1].previousSibling
  804. is root.childNodes[1].childNodes[0]
  805. and root.childNodes[1].childNodes[0].previousSibling is None
  806. and root.childNodes[1].childNodes[0].nextSibling
  807. is root.childNodes[1].childNodes[1]
  808. , "testNormalize2 -- sibling pointers")
  809. doc.unlink()
  810. def testBug0777884(self):
  811. doc = parseString("<o>text</o>")
  812. text = doc.documentElement.childNodes[0]
  813. self.assertEqual(text.nodeType, Node.TEXT_NODE)
  814. # Should run quietly, doing nothing.
  815. text.normalize()
  816. doc.unlink()
  817. def testBug1433694(self):
  818. doc = parseString("<o><i/>t</o>")
  819. node = doc.documentElement
  820. node.childNodes[1].nodeValue = ""
  821. node.normalize()
  822. self.confirm(node.childNodes[-1].nextSibling is None,
  823. "Final child's .nextSibling should be None")
  824. def testSiblings(self):
  825. doc = parseString("<doc><?pi?>text?<elm/></doc>")
  826. root = doc.documentElement
  827. (pi, text, elm) = root.childNodes
  828. self.confirm(pi.nextSibling is text and
  829. pi.previousSibling is None and
  830. text.nextSibling is elm and
  831. text.previousSibling is pi and
  832. elm.nextSibling is None and
  833. elm.previousSibling is text, "testSiblings")
  834. doc.unlink()
  835. def testParents(self):
  836. doc = parseString(
  837. "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>")
  838. root = doc.documentElement
  839. elm1 = root.childNodes[0]
  840. (elm2a, elm2b) = elm1.childNodes
  841. elm3 = elm2b.childNodes[0]
  842. self.confirm(root.parentNode is doc and
  843. elm1.parentNode is root and
  844. elm2a.parentNode is elm1 and
  845. elm2b.parentNode is elm1 and
  846. elm3.parentNode is elm2b, "testParents")
  847. doc.unlink()
  848. def testNodeListItem(self):
  849. doc = parseString("<doc><e/><e/></doc>")
  850. children = doc.childNodes
  851. docelem = children[0]
  852. self.confirm(children[0] is children.item(0)
  853. and children.item(1) is None
  854. and docelem.childNodes.item(0) is docelem.childNodes[0]
  855. and docelem.childNodes.item(1) is docelem.childNodes[1]
  856. and docelem.childNodes.item(0).childNodes.item(0) is None,
  857. "test NodeList.item()")
  858. doc.unlink()
  859. def testSAX2DOM(self):
  860. from xml.dom import pulldom
  861. sax2dom = pulldom.SAX2DOM()
  862. sax2dom.startDocument()
  863. sax2dom.startElement("doc", {})
  864. sax2dom.characters("text")
  865. sax2dom.startElement("subelm", {})
  866. sax2dom.characters("text")
  867. sax2dom.endElement("subelm")
  868. sax2dom.characters("text")
  869. sax2dom.endElement("doc")
  870. sax2dom.endDocument()
  871. doc = sax2dom.document
  872. root = doc.documentElement
  873. (text1, elm1, text2) = root.childNodes
  874. text3 = elm1.childNodes[0]
  875. self.confirm(text1.previousSibling is None and
  876. text1.nextSibling is elm1 and
  877. elm1.previousSibling is text1 and
  878. elm1.nextSibling is text2 and
  879. text2.previousSibling is elm1 and
  880. text2.nextSibling is None and
  881. text3.previousSibling is None and
  882. text3.nextSibling is None, "testSAX2DOM - siblings")
  883. self.confirm(root.parentNode is doc and
  884. text1.parentNode is root and
  885. elm1.parentNode is root and
  886. text2.parentNode is root and
  887. text3.parentNode is elm1, "testSAX2DOM - parents")
  888. doc.unlink()
  889. def testEncodings(self):
  890. doc = parseString('<foo>&#x20ac;</foo>')
  891. self.confirm(doc.toxml() == u'<?xml version="1.0" ?><foo>\u20ac</foo>'
  892. and doc.toxml('utf-8') ==
  893. '<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>'
  894. and doc.toxml('iso-8859-15') ==
  895. '<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>',
  896. "testEncodings - encoding EURO SIGN")
  897. # Verify that character decoding errors raise exceptions instead
  898. # of crashing
  899. self.assertRaises(UnicodeDecodeError, parseString,
  900. '<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
  901. doc.unlink()
  902. class UserDataHandler:
  903. called = 0
  904. def handle(self, operation, key, data, src, dst):
  905. dst.setUserData(key, data + 1, self)
  906. src.setUserData(key, None, None)
  907. self.called = 1
  908. def testUserData(self):
  909. dom = Document()
  910. n = dom.createElement('e')
  911. self.confirm(n.getUserData("foo") is None)
  912. n.setUserData("foo", None, None)
  913. self.confirm(n.getUserData("foo") is None)
  914. n.setUserData("foo", 12, 12)
  915. n.setUserData("bar", 13, 13)
  916. self.confirm(n.getUserData("foo") == 12)
  917. self.confirm(n.getUserData("bar") == 13)
  918. n.setUserData("foo", None, None)
  919. self.confirm(n.getUserData("foo") is None)
  920. self.confirm(n.getUserData("bar") == 13)
  921. handler = self.UserDataHandler()
  922. n.setUserData("bar", 12, handler)
  923. c = n.cloneNode(1)
  924. self.confirm(handler.called
  925. and n.getUserData("bar") is None
  926. and c.getUserData("bar") == 13)
  927. n.unlink()
  928. c.unlink()
  929. dom.unlink()
  930. def checkRenameNodeSharedConstraints(self, doc, node):
  931. # Make sure illegal NS usage is detected:
  932. self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
  933. "http://xml.python.org/ns", "xmlns:foo")
  934. doc2 = parseString("<doc/>")
  935. self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
  936. xml.dom.EMPTY_NAMESPACE, "foo")
  937. def testRenameAttribute(self):
  938. doc = parseString("<doc a='v'/>")
  939. elem = doc.documentElement
  940. attrmap = elem.attributes
  941. attr = elem.attributes['a']
  942. # Simple renaming
  943. attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
  944. self.confirm(attr.name == "b"
  945. and attr.nodeName == "b"
  946. and attr.localName is None
  947. and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
  948. and attr.prefix is None
  949. and attr.value == "v"
  950. and elem.getAttributeNode("a") is None
  951. and elem.getAttributeNode("b").isSameNode(attr)
  952. and attrmap["b"].isSameNode(attr)
  953. and attr.ownerDocument.isSameNode(doc)
  954. and attr.ownerElement.isSameNode(elem))
  955. # Rename to have a namespace, no prefix
  956. attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
  957. self.confirm(attr.name == "c"
  958. and attr.nodeName == "c"
  959. and attr.localName == "c"
  960. and attr.namespaceURI == "http://xml.python.org/ns"
  961. and attr.prefix is None
  962. and attr.value == "v"
  963. and elem.getAttributeNode("a") is None
  964. and elem.getAttributeNode("b") is None
  965. and elem.getAttributeNode("c").isSameNode(attr)
  966. and elem.getAttributeNodeNS(
  967. "http://xml.python.org/ns", "c").isSameNode(attr)
  968. and attrmap["c"].isSameNode(attr)
  969. and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
  970. # Rename to have a namespace, with prefix
  971. attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
  972. self.confirm(attr.name == "p:d"
  973. and attr.nodeName == "p:d"
  974. and attr.localName == "d"
  975. and attr.namespaceURI == "http://xml.python.org/ns2"
  976. and attr.prefix == "p"
  977. and attr.value == "v"
  978. and elem.getAttributeNode("a") is None
  979. and elem.getAttributeNode("b") is None
  980. and elem.getAttributeNode("c") is None
  981. and elem.getAttributeNodeNS(
  982. "http://xml.python.org/ns", "c") is None
  983. and elem.getAttributeNode("p:d").isSameNode(attr)
  984. and elem.getAttributeNodeNS(
  985. "http://xml.python.org/ns2", "d").isSameNode(attr)
  986. and attrmap["p:d"].isSameNode(attr)
  987. and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
  988. # Rename back to a simple non-NS node
  989. attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
  990. self.confirm(attr.name == "e"
  991. and attr.nodeName == "e"
  992. and attr.localName is None
  993. and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
  994. and attr.prefix is None
  995. and attr.value == "v"
  996. and elem.getAttributeNode("a") is None
  997. and elem.getAttributeNode("b") is None
  998. and elem.getAttributeNode("c") is None
  999. and elem.getAttributeNode("p:d") is None
  1000. and elem.getAttributeNodeNS(
  1001. "http://xml.python.org/ns", "c") is None
  1002. and elem.getAttributeNode("e").isSameNode(attr)
  1003. and attrmap["e"].isSameNode(attr))
  1004. self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
  1005. "http://xml.python.org/ns", "xmlns")
  1006. self.checkRenameNodeSharedConstraints(doc, attr)
  1007. doc.unlink()
  1008. def testRenameElement(self):
  1009. doc = parseString("<doc/>")
  1010. elem = doc.documentElement
  1011. # Simple renaming
  1012. elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
  1013. self.confirm(elem.tagName == "a"
  1014. and elem.nodeName == "a"
  1015. and elem.localName is None
  1016. and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
  1017. and elem.prefix is None
  1018. and elem.ownerDocument.isSameNode(doc))
  1019. # Rename to have a namespace, no prefix
  1020. elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
  1021. self.confirm(elem.tagName == "b"
  1022. and elem.nodeName == "b"
  1023. and elem.localName == "b"
  1024. and elem.namespaceURI == "http://xml.python.org/ns"
  1025. and elem.prefix is None
  1026. and elem.ownerDocument.isSameNode(doc))
  1027. # Rename to have a namespace, with prefix
  1028. elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
  1029. self.confirm(elem.tagName == "p:c"
  1030. and elem.nodeName == "p:c"
  1031. and elem.localName == "c"
  1032. and elem.namespaceURI == "http://xml.python.org/ns2"
  1033. and elem.prefix == "p"
  1034. and elem.ownerDocument.isSameNode(doc))
  1035. # Rename back to a simple non-NS node
  1036. elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
  1037. self.confirm(elem.tagName == "d"
  1038. and elem.nodeName == "d"
  1039. and elem.localName is None
  1040. and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
  1041. and elem.prefix is None
  1042. and elem.ownerDocument.isSameNode(doc))
  1043. self.checkRenameNodeSharedConstraints(doc, elem)
  1044. doc.unlink()
  1045. def testRenameOther(self):
  1046. # We have to create a comment node explicitly since not all DOM
  1047. # builders used with minidom add comments to the DOM.
  1048. doc = xml.dom.minidom.getDOMImplementation().createDocument(
  1049. xml.dom.EMPTY_NAMESPACE, "e", None)
  1050. node = doc.createComment("comment")
  1051. self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
  1052. xml.dom.EMPTY_NAMESPACE, "foo")
  1053. doc.unlink()
  1054. def testWholeText(self):
  1055. doc = parseString("<doc>a</doc>")
  1056. elem = doc.documentElement
  1057. text = elem.childNodes[0]
  1058. self.assertEqual(text.nodeType, Node.TEXT_NODE)
  1059. self.checkWholeText(text, "a")
  1060. elem.appendChild(doc.createTextNode("b"))
  1061. self.checkWholeText(text, "ab")
  1062. elem.insertBefore(doc.createCDATASection("c"), text)
  1063. self.checkWholeText(text, "cab")
  1064. # make sure we don't cross other nodes
  1065. splitter = doc.createComment("comment")
  1066. elem.appendChild(splitter)
  1067. text2 = doc.createTextNode("d")
  1068. elem.appendChild(text2)
  1069. self.checkWholeText(text, "cab")
  1070. self.checkWholeText(text2, "d")
  1071. x = doc.createElement("x")
  1072. elem.replaceChild(x, splitter)
  1073. splitter = x
  1074. self.checkWholeText(text, "cab")
  1075. self.checkWholeText(text2, "d")
  1076. x = doc.createProcessingInstruction("y", "z")
  1077. elem.replaceChild(x, splitter)
  1078. splitter = x
  1079. self.checkWholeText(text, "cab")
  1080. self.checkWholeText(text2, "d")
  1081. elem.removeChild(splitter)
  1082. self.checkWholeText(text, "cabd")
  1083. self.checkWholeText(text2, "cabd")
  1084. def testPatch1094164(self):
  1085. doc = parseString("<doc><e/></doc>")
  1086. elem = doc.documentElement
  1087. e = elem.firstChild
  1088. self.confirm(e.parentNode is elem, "Before replaceChild()")
  1089. # Check that replacing a child with itself leaves the tree unchanged
  1090. elem.replaceChild(e, e)
  1091. self.confirm(e.parentNode is elem, "After replaceChild()")
  1092. def testReplaceWholeText(self):
  1093. def setup():
  1094. doc = parseString("<doc>a<e/>d</doc>")
  1095. elem = doc.documentElement
  1096. text1 = elem.firstChild
  1097. text2 = elem.lastChild
  1098. splitter = text1.nextSibling
  1099. elem.insertBefore(doc.createTextNode("b"), splitter)
  1100. elem.insertBefore(doc.createCDATASection("c"), text1)
  1101. return doc, elem, text1, splitter, text2
  1102. doc, elem, text1, splitter, text2 = setup()
  1103. text = text1.replaceWholeText("new content")
  1104. self.checkWholeText(text, "new content")
  1105. self.checkWholeText(text2, "d")
  1106. self.confirm(len(elem.childNodes) == 3)
  1107. doc, elem, text1, splitter, text2 = setup()
  1108. text = text2.replaceWholeText("new content")
  1109. self.checkWholeText(text, "new content")
  1110. self.checkWholeText(text1, "cab")
  1111. self.confirm(len(elem.childNodes) == 5)
  1112. doc, elem, text1, splitter, text2 = setup()
  1113. text = text1.replaceWholeText("")
  1114. self.checkWholeText(text2, "d")
  1115. self.confirm(text is None
  1116. and len(elem.childNodes) == 2)
  1117. def testSchemaType(self):
  1118. doc = parseString(
  1119. "<!DOCTYPE doc [\n"
  1120. " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
  1121. " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
  1122. " <!ATTLIST doc id ID #IMPLIED \n"
  1123. " ref IDREF #IMPLIED \n"
  1124. " refs IDREFS #IMPLIED \n"
  1125. " enum (a|b) #IMPLIED \n"
  1126. " ent ENTITY #IMPLIED \n"
  1127. " ents ENTITIES #IMPLIED \n"
  1128. " nm NMTOKEN #IMPLIED \n"
  1129. " nms NMTOKENS #IMPLIED \n"
  1130. " text CDATA #IMPLIED \n"
  1131. " >\n"
  1132. "]><doc id='name' notid='name' text='splat!' enum='b'"
  1133. " ref='name' refs='name name' ent='e1' ents='e1 e2'"
  1134. " nm='123' nms='123 abc' />")
  1135. elem = doc.documentElement
  1136. # We don't want to rely on any specific loader at this point, so
  1137. # just make sure we can get to all the names, and that the
  1138. # DTD-based namespace is right. The names can vary by loader
  1139. # since each supports a different level of DTD information.
  1140. t = elem.schemaType
  1141. self.confirm(t.name is None
  1142. and t.namespace == xml.dom.EMPTY_NAMESPACE)
  1143. names = "id notid text enum ref refs ent ents nm nms".split()
  1144. for name in names:
  1145. a = elem.getAttributeNode(name)
  1146. t = a.schemaType
  1147. self.confirm(hasattr(t, "name")
  1148. and t.namespace == xml.dom.EMPTY_NAMESPACE)
  1149. def testSetIdAttribute(self):
  1150. doc = parseString("<doc a1='v' a2='w'/>")
  1151. e = doc.documentElement
  1152. a1 = e.getAttributeNode("a1")
  1153. a2 = e.getAttributeNode("a2")
  1154. self.confirm(doc.getElementById("v") is None
  1155. and not a1.isId
  1156. and not a2.isId)
  1157. e.setIdAttribute("a1")
  1158. self.confirm(e.isSameNode(doc.getElementById("v"))
  1159. and a1.isId
  1160. and not a2.isId)
  1161. e.setIdAttribute("a2")
  1162. self.confirm(e.isSameNode(doc.getElementById("v"))
  1163. and e.isSameNode(doc.getElementById("w"))
  1164. and a1.isId
  1165. and a2.isId)
  1166. # replace the a1 node; the new node should *not* be an ID
  1167. a3 = doc.createAttribute("a1")
  1168. a3.value = "v"
  1169. e.setAttributeNode(a3)
  1170. self.confirm(doc.getElementById("v") is None
  1171. and e.isSameNode(doc.getElementById("w"))
  1172. and not a1.isId
  1173. and a2.isId
  1174. and not a3.isId)
  1175. # renaming an attribute should not affect its ID-ness:
  1176. doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
  1177. self.confirm(e.isSameNode(doc.getElementById("w"))
  1178. and a2.isId)
  1179. def testSetIdAttributeNS(self):
  1180. NS1 = "http://xml.python.org/ns1"
  1181. NS2 = "http://xml.python.org/ns2"
  1182. doc = parseString("<doc"
  1183. " xmlns:ns1='" + NS1 + "'"
  1184. " xmlns:ns2='" + NS2 + "'"
  1185. " ns1:a1='v' ns2:a2='w'/>")
  1186. e = doc.documentElement
  1187. a1 = e.getAttributeNodeNS(NS1, "a1")
  1188. a2 = e.getAttributeNodeNS(NS2, "a2")
  1189. self.confirm(doc.getElementById("v") is None
  1190. and not a1.isId
  1191. and not a2.isId)
  1192. e.setIdAttributeNS(NS1, "a1")
  1193. self.confirm(e.isSameNode(doc.getElementById("v"))
  1194. and a1.isId
  1195. and not a2.isId)
  1196. e.setIdAttributeNS(NS2, "a2")
  1197. self.confirm(e.isSameNode(doc.getElementById("v"))
  1198. and e.isSameNode(doc.getElementById("w"))
  1199. and a1.isId
  1200. and a2.isId)
  1201. # replace the a1 node; the new node should *not* be an ID
  1202. a3 = doc.createAttributeNS(NS1, "a1")
  1203. a3.value = "v"
  1204. e.setAttributeNode(a3)
  1205. self.confirm(e.isSameNode(doc.getElementById("w")))
  1206. self.confirm(not a1.isId)
  1207. self.confirm(a2.isId)
  1208. self.confirm(not a3.isId)
  1209. self.confirm(doc.getElementById("v") is None)
  1210. # renaming an attribute should not affect its ID-ness:
  1211. doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
  1212. self.confirm(e.isSameNode(doc.getElementById("w"))
  1213. and a2.isId)
  1214. def testSetIdAttributeNode(self):
  1215. NS1 = "http://xml.python.org/ns1"
  1216. NS2 = "http://xml.python.org/ns2"
  1217. doc = parseString("<doc"
  1218. " xmlns:ns1='" + NS1 + "'"
  1219. " xmlns:ns2='" + NS2 + "'"
  1220. " ns1:a1='v' ns2:a2='w'/>")
  1221. e = doc.documentElement
  1222. a1 = e.getAttributeNodeNS(NS1, "a1")
  1223. a2 = e.getAttributeNodeNS(NS2, "a2")
  1224. self.confirm(doc.getElementById("v") is None
  1225. and not a1.isId
  1226. and not a2.isId)
  1227. e.setIdAttributeNode(a1)
  1228. self.confirm(e.isSameNode(doc.getElementById("v"))
  1229. and a1.isId
  1230. and not a2.isId)
  1231. e.setIdAttributeNode(a2)
  1232. self.confirm(e.isSameNode(doc.getElementById("v"))
  1233. and e.isSameNode(doc.getElementById("w"))
  1234. and a1.isId
  1235. and a2.isId)
  1236. # replace the a1 node; the new node should *not* be an ID
  1237. a3 = doc.createAttributeNS(NS1, "a1")
  1238. a3.value = "v"
  1239. e.setAttributeNode(a3)
  1240. self.confirm(e.isSameNode(doc.getElementById("w")))
  1241. self.confirm(not a1.isId)
  1242. self.confirm(a2.isId)
  1243. self.confirm(not a3.isId)
  1244. self.confirm(doc.getElementById("v") is None)
  1245. # renaming an attribute should not affect its ID-ness:
  1246. doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
  1247. self.confirm(e.isSameNode(doc.getElementById("w"))
  1248. and a2.isId)
  1249. def assert_recursive_equal(self, doc, doc2):
  1250. stack = [(doc, doc2)]
  1251. while stack:
  1252. n1, n2 = stack.pop()
  1253. self.assertEqual(n1.nodeType, n2.nodeType)
  1254. self.assertEqual(len(n1.childNodes), len(n2.childNodes))
  1255. self.assertEqual(n1.nodeName, n2.nodeName)
  1256. self.assertFalse(n1.isSameNode(n2))
  1257. self.assertFalse(n2.isSameNode(n1))
  1258. if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
  1259. len(n1.entities)
  1260. len(n2.entities)
  1261. len(n1.notations)
  1262. len(n2.notations)
  1263. self.assertEqual(len(n1.entities), len(n2.entities))
  1264. self.assertEqual(len(n1.notations), len(n2.notations))
  1265. for i in range(len(n1.notations)):
  1266. # XXX this loop body doesn't seem to be executed?
  1267. no1 = n1.notations.item(i)
  1268. no2 = n1.notations.item(i)
  1269. self.assertEqual(no1.name, no2.name)
  1270. self.assertEqual(no1.publicId, no2.publicId)
  1271. self.assertEqual(no1.systemId, no2.systemId)
  1272. stack.append((no1, no2))
  1273. for i in range(len(n1.entities)):
  1274. e1 = n1.entities.item(i)
  1275. e2 = n2.entities.item(i)
  1276. self.assertEqual(e1.notationName, e2.notationName)
  1277. self.assertEqual(e1.publicId, e2.publicId)
  1278. self.assertEqual(e1.systemId, e2.systemId)
  1279. stack.append((e1, e2))
  1280. if n1.nodeType != Node.DOCUMENT_NODE:
  1281. self.assertTrue(n1.ownerDocument.isSameNode(doc))
  1282. self.assertTrue(n2.ownerDocument.isSameNode(doc2))
  1283. for i in range(len(n1.childNodes)):
  1284. stack.append((n1.childNodes[i], n2.childNodes[i]))
  1285. def testPickledDocument(self):
  1286. doc = parseString(sample)
  1287. for proto in range(pickle.HIGHEST_PROTOCOL + 1):
  1288. s = pickle.dumps(doc, proto)
  1289. doc2 = pickle.loads(s)
  1290. self.assert_recursive_equal(doc, doc2)
  1291. def testDeepcopiedDocument(self):
  1292. doc = parseString(sample)
  1293. doc2 = copy.deepcopy(doc)
  1294. self.assert_recursive_equal(doc, doc2)
  1295. def testSerializeCommentNodeWithDoubleHyphen(self):
  1296. doc = create_doc_without_doctype()
  1297. doc.appendChild(doc.createComment("foo--bar"))
  1298. self.assertRaises(ValueError, doc.toxml)
  1299. def testEmptyXMLNSValue(self):
  1300. doc = parseString("<element xmlns=''>\n"
  1301. "<foo/>\n</element>")
  1302. doc2 = parseString(doc.toxml())
  1303. self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
  1304. def test_main():
  1305. run_unittest(MinidomTest)
  1306. if __name__ == "__main__":
  1307. test_main()