PageRenderTime 73ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/python/lib/Lib/xmllib.py

http://github.com/JetBrains/intellij-community
Python | 929 lines | 850 code | 37 blank | 42 comment | 147 complexity | e19645d51d8fe9044bdf2dafd5837405 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, MPL-2.0-no-copyleft-exception, MIT, EPL-1.0, AGPL-1.0
  1. """A parser for XML, using the derived class as static DTD."""
  2. # Author: Sjoerd Mullender.
  3. import re
  4. import string
  5. import warnings
  6. warnings.warn("The xmllib module is obsolete. Use xml.sax instead.", DeprecationWarning)
  7. del warnings
  8. version = '0.3'
  9. class Error(RuntimeError):
  10. pass
  11. # Regular expressions used for parsing
  12. _S = '[ \t\r\n]+' # white space
  13. _opS = '[ \t\r\n]*' # optional white space
  14. _Name = '[a-zA-Z_:][-a-zA-Z0-9._:]*' # valid XML name
  15. _QStr = "(?:'[^']*'|\"[^\"]*\")" # quoted XML string
  16. illegal = re.compile('[^\t\r\n -\176\240-\377]') # illegal chars in content
  17. interesting = re.compile('[]&<]')
  18. amp = re.compile('&')
  19. ref = re.compile('&(' + _Name + '|#[0-9]+|#x[0-9a-fA-F]+)[^-a-zA-Z0-9._:]')
  20. entityref = re.compile('&(?P<name>' + _Name + ')[^-a-zA-Z0-9._:]')
  21. charref = re.compile('&#(?P<char>[0-9]+[^0-9]|x[0-9a-fA-F]+[^0-9a-fA-F])')
  22. space = re.compile(_S + '$')
  23. newline = re.compile('\n')
  24. attrfind = re.compile(
  25. _S + '(?P<name>' + _Name + ')'
  26. '(' + _opS + '=' + _opS +
  27. '(?P<value>'+_QStr+'|[-a-zA-Z0-9.:+*%?!\(\)_#=~]+))?')
  28. starttagopen = re.compile('<' + _Name)
  29. starttagend = re.compile(_opS + '(?P<slash>/?)>')
  30. starttagmatch = re.compile('<(?P<tagname>'+_Name+')'
  31. '(?P<attrs>(?:'+attrfind.pattern+')*)'+
  32. starttagend.pattern)
  33. endtagopen = re.compile('</')
  34. endbracket = re.compile(_opS + '>')
  35. endbracketfind = re.compile('(?:[^>\'"]|'+_QStr+')*>')
  36. tagfind = re.compile(_Name)
  37. cdataopen = re.compile(r'<!\[CDATA\[')
  38. cdataclose = re.compile(r'\]\]>')
  39. # this matches one of the following:
  40. # SYSTEM SystemLiteral
  41. # PUBLIC PubidLiteral SystemLiteral
  42. _SystemLiteral = '(?P<%s>'+_QStr+')'
  43. _PublicLiteral = '(?P<%s>"[-\'\(\)+,./:=?;!*#@$_%% \n\ra-zA-Z0-9]*"|' \
  44. "'[-\(\)+,./:=?;!*#@$_%% \n\ra-zA-Z0-9]*')"
  45. _ExternalId = '(?:SYSTEM|' \
  46. 'PUBLIC'+_S+_PublicLiteral%'pubid'+ \
  47. ')'+_S+_SystemLiteral%'syslit'
  48. doctype = re.compile('<!DOCTYPE'+_S+'(?P<name>'+_Name+')'
  49. '(?:'+_S+_ExternalId+')?'+_opS)
  50. xmldecl = re.compile('<\?xml'+_S+
  51. 'version'+_opS+'='+_opS+'(?P<version>'+_QStr+')'+
  52. '(?:'+_S+'encoding'+_opS+'='+_opS+
  53. "(?P<encoding>'[A-Za-z][-A-Za-z0-9._]*'|"
  54. '"[A-Za-z][-A-Za-z0-9._]*"))?'
  55. '(?:'+_S+'standalone'+_opS+'='+_opS+
  56. '(?P<standalone>\'(?:yes|no)\'|"(?:yes|no)"))?'+
  57. _opS+'\?>')
  58. procopen = re.compile(r'<\?(?P<proc>' + _Name + ')' + _opS)
  59. procclose = re.compile(_opS + r'\?>')
  60. commentopen = re.compile('<!--')
  61. commentclose = re.compile('-->')
  62. doubledash = re.compile('--')
  63. attrtrans = string.maketrans(' \r\n\t', ' ')
  64. # definitions for XML namespaces
  65. _NCName = '[a-zA-Z_][-a-zA-Z0-9._]*' # XML Name, minus the ":"
  66. ncname = re.compile(_NCName + '$')
  67. qname = re.compile('(?:(?P<prefix>' + _NCName + '):)?' # optional prefix
  68. '(?P<local>' + _NCName + ')$')
  69. xmlns = re.compile('xmlns(?::(?P<ncname>'+_NCName+'))?$')
  70. # XML parser base class -- find tags and call handler functions.
  71. # Usage: p = XMLParser(); p.feed(data); ...; p.close().
  72. # The dtd is defined by deriving a class which defines methods with
  73. # special names to handle tags: start_foo and end_foo to handle <foo>
  74. # and </foo>, respectively. The data between tags is passed to the
  75. # parser by calling self.handle_data() with some data as argument (the
  76. # data may be split up in arbitrary chunks).
  77. class XMLParser:
  78. attributes = {} # default, to be overridden
  79. elements = {} # default, to be overridden
  80. # parsing options, settable using keyword args in __init__
  81. __accept_unquoted_attributes = 0
  82. __accept_missing_endtag_name = 0
  83. __map_case = 0
  84. __accept_utf8 = 0
  85. __translate_attribute_references = 1
  86. # Interface -- initialize and reset this instance
  87. def __init__(self, **kw):
  88. self.__fixed = 0
  89. if 'accept_unquoted_attributes' in kw:
  90. self.__accept_unquoted_attributes = kw['accept_unquoted_attributes']
  91. if 'accept_missing_endtag_name' in kw:
  92. self.__accept_missing_endtag_name = kw['accept_missing_endtag_name']
  93. if 'map_case' in kw:
  94. self.__map_case = kw['map_case']
  95. if 'accept_utf8' in kw:
  96. self.__accept_utf8 = kw['accept_utf8']
  97. if 'translate_attribute_references' in kw:
  98. self.__translate_attribute_references = kw['translate_attribute_references']
  99. self.reset()
  100. def __fixelements(self):
  101. self.__fixed = 1
  102. self.elements = {}
  103. self.__fixdict(self.__dict__)
  104. self.__fixclass(self.__class__)
  105. def __fixclass(self, kl):
  106. self.__fixdict(kl.__dict__)
  107. for k in kl.__bases__:
  108. self.__fixclass(k)
  109. def __fixdict(self, dict):
  110. for key in dict.keys():
  111. if key[:6] == 'start_':
  112. tag = key[6:]
  113. start, end = self.elements.get(tag, (None, None))
  114. if start is None:
  115. self.elements[tag] = getattr(self, key), end
  116. elif key[:4] == 'end_':
  117. tag = key[4:]
  118. start, end = self.elements.get(tag, (None, None))
  119. if end is None:
  120. self.elements[tag] = start, getattr(self, key)
  121. # Interface -- reset this instance. Loses all unprocessed data
  122. def reset(self):
  123. self.rawdata = ''
  124. self.stack = []
  125. self.nomoretags = 0
  126. self.literal = 0
  127. self.lineno = 1
  128. self.__at_start = 1
  129. self.__seen_doctype = None
  130. self.__seen_starttag = 0
  131. self.__use_namespaces = 0
  132. self.__namespaces = {'xml':None} # xml is implicitly declared
  133. # backward compatibility hack: if elements not overridden,
  134. # fill it in ourselves
  135. if self.elements is XMLParser.elements:
  136. self.__fixelements()
  137. # For derived classes only -- enter literal mode (CDATA) till EOF
  138. def setnomoretags(self):
  139. self.nomoretags = self.literal = 1
  140. # For derived classes only -- enter literal mode (CDATA)
  141. def setliteral(self, *args):
  142. self.literal = 1
  143. # Interface -- feed some data to the parser. Call this as
  144. # often as you want, with as little or as much text as you
  145. # want (may include '\n'). (This just saves the text, all the
  146. # processing is done by goahead().)
  147. def feed(self, data):
  148. self.rawdata = self.rawdata + data
  149. self.goahead(0)
  150. # Interface -- handle the remaining data
  151. def close(self):
  152. self.goahead(1)
  153. if self.__fixed:
  154. self.__fixed = 0
  155. # remove self.elements so that we don't leak
  156. del self.elements
  157. # Interface -- translate references
  158. def translate_references(self, data, all = 1):
  159. if not self.__translate_attribute_references:
  160. return data
  161. i = 0
  162. while 1:
  163. res = amp.search(data, i)
  164. if res is None:
  165. return data
  166. s = res.start(0)
  167. res = ref.match(data, s)
  168. if res is None:
  169. self.syntax_error("bogus `&'")
  170. i = s+1
  171. continue
  172. i = res.end(0)
  173. str = res.group(1)
  174. rescan = 0
  175. if str[0] == '#':
  176. if str[1] == 'x':
  177. str = chr(int(str[2:], 16))
  178. else:
  179. str = chr(int(str[1:]))
  180. if data[i - 1] != ';':
  181. self.syntax_error("`;' missing after char reference")
  182. i = i-1
  183. elif all:
  184. if str in self.entitydefs:
  185. str = self.entitydefs[str]
  186. rescan = 1
  187. elif data[i - 1] != ';':
  188. self.syntax_error("bogus `&'")
  189. i = s + 1 # just past the &
  190. continue
  191. else:
  192. self.syntax_error("reference to unknown entity `&%s;'" % str)
  193. str = '&' + str + ';'
  194. elif data[i - 1] != ';':
  195. self.syntax_error("bogus `&'")
  196. i = s + 1 # just past the &
  197. continue
  198. # when we get here, str contains the translated text and i points
  199. # to the end of the string that is to be replaced
  200. data = data[:s] + str + data[i:]
  201. if rescan:
  202. i = s
  203. else:
  204. i = s + len(str)
  205. # Interface - return a dictionary of all namespaces currently valid
  206. def getnamespace(self):
  207. nsdict = {}
  208. for t, d, nst in self.stack:
  209. nsdict.update(d)
  210. return nsdict
  211. # Internal -- handle data as far as reasonable. May leave state
  212. # and data to be processed by a subsequent call. If 'end' is
  213. # true, force handling all data as if followed by EOF marker.
  214. def goahead(self, end):
  215. rawdata = self.rawdata
  216. i = 0
  217. n = len(rawdata)
  218. while i < n:
  219. if i > 0:
  220. self.__at_start = 0
  221. if self.nomoretags:
  222. data = rawdata[i:n]
  223. self.handle_data(data)
  224. self.lineno = self.lineno + data.count('\n')
  225. i = n
  226. break
  227. res = interesting.search(rawdata, i)
  228. if res:
  229. j = res.start(0)
  230. else:
  231. j = n
  232. if i < j:
  233. data = rawdata[i:j]
  234. if self.__at_start and space.match(data) is None:
  235. self.syntax_error('illegal data at start of file')
  236. self.__at_start = 0
  237. if not self.stack and space.match(data) is None:
  238. self.syntax_error('data not in content')
  239. if not self.__accept_utf8 and illegal.search(data):
  240. self.syntax_error('illegal character in content')
  241. self.handle_data(data)
  242. self.lineno = self.lineno + data.count('\n')
  243. i = j
  244. if i == n: break
  245. if rawdata[i] == '<':
  246. if starttagopen.match(rawdata, i):
  247. if self.literal:
  248. data = rawdata[i]
  249. self.handle_data(data)
  250. self.lineno = self.lineno + data.count('\n')
  251. i = i+1
  252. continue
  253. k = self.parse_starttag(i)
  254. if k < 0: break
  255. self.__seen_starttag = 1
  256. self.lineno = self.lineno + rawdata[i:k].count('\n')
  257. i = k
  258. continue
  259. if endtagopen.match(rawdata, i):
  260. k = self.parse_endtag(i)
  261. if k < 0: break
  262. self.lineno = self.lineno + rawdata[i:k].count('\n')
  263. i = k
  264. continue
  265. if commentopen.match(rawdata, i):
  266. if self.literal:
  267. data = rawdata[i]
  268. self.handle_data(data)
  269. self.lineno = self.lineno + data.count('\n')
  270. i = i+1
  271. continue
  272. k = self.parse_comment(i)
  273. if k < 0: break
  274. self.lineno = self.lineno + rawdata[i:k].count('\n')
  275. i = k
  276. continue
  277. if cdataopen.match(rawdata, i):
  278. k = self.parse_cdata(i)
  279. if k < 0: break
  280. self.lineno = self.lineno + rawdata[i:k].count('\n')
  281. i = k
  282. continue
  283. res = xmldecl.match(rawdata, i)
  284. if res:
  285. if not self.__at_start:
  286. self.syntax_error("<?xml?> declaration not at start of document")
  287. version, encoding, standalone = res.group('version',
  288. 'encoding',
  289. 'standalone')
  290. if version[1:-1] != '1.0':
  291. raise Error('only XML version 1.0 supported')
  292. if encoding: encoding = encoding[1:-1]
  293. if standalone: standalone = standalone[1:-1]
  294. self.handle_xml(encoding, standalone)
  295. i = res.end(0)
  296. continue
  297. res = procopen.match(rawdata, i)
  298. if res:
  299. k = self.parse_proc(i)
  300. if k < 0: break
  301. self.lineno = self.lineno + rawdata[i:k].count('\n')
  302. i = k
  303. continue
  304. res = doctype.match(rawdata, i)
  305. if res:
  306. if self.literal:
  307. data = rawdata[i]
  308. self.handle_data(data)
  309. self.lineno = self.lineno + data.count('\n')
  310. i = i+1
  311. continue
  312. if self.__seen_doctype:
  313. self.syntax_error('multiple DOCTYPE elements')
  314. if self.__seen_starttag:
  315. self.syntax_error('DOCTYPE not at beginning of document')
  316. k = self.parse_doctype(res)
  317. if k < 0: break
  318. self.__seen_doctype = res.group('name')
  319. if self.__map_case:
  320. self.__seen_doctype = self.__seen_doctype.lower()
  321. self.lineno = self.lineno + rawdata[i:k].count('\n')
  322. i = k
  323. continue
  324. elif rawdata[i] == '&':
  325. if self.literal:
  326. data = rawdata[i]
  327. self.handle_data(data)
  328. i = i+1
  329. continue
  330. res = charref.match(rawdata, i)
  331. if res is not None:
  332. i = res.end(0)
  333. if rawdata[i-1] != ';':
  334. self.syntax_error("`;' missing in charref")
  335. i = i-1
  336. if not self.stack:
  337. self.syntax_error('data not in content')
  338. self.handle_charref(res.group('char')[:-1])
  339. self.lineno = self.lineno + res.group(0).count('\n')
  340. continue
  341. res = entityref.match(rawdata, i)
  342. if res is not None:
  343. i = res.end(0)
  344. if rawdata[i-1] != ';':
  345. self.syntax_error("`;' missing in entityref")
  346. i = i-1
  347. name = res.group('name')
  348. if self.__map_case:
  349. name = name.lower()
  350. if name in self.entitydefs:
  351. self.rawdata = rawdata = rawdata[:res.start(0)] + self.entitydefs[name] + rawdata[i:]
  352. n = len(rawdata)
  353. i = res.start(0)
  354. else:
  355. self.unknown_entityref(name)
  356. self.lineno = self.lineno + res.group(0).count('\n')
  357. continue
  358. elif rawdata[i] == ']':
  359. if self.literal:
  360. data = rawdata[i]
  361. self.handle_data(data)
  362. i = i+1
  363. continue
  364. if n-i < 3:
  365. break
  366. if cdataclose.match(rawdata, i):
  367. self.syntax_error("bogus `]]>'")
  368. self.handle_data(rawdata[i])
  369. i = i+1
  370. continue
  371. else:
  372. raise Error('neither < nor & ??')
  373. # We get here only if incomplete matches but
  374. # nothing else
  375. break
  376. # end while
  377. if i > 0:
  378. self.__at_start = 0
  379. if end and i < n:
  380. data = rawdata[i]
  381. self.syntax_error("bogus `%s'" % data)
  382. if not self.__accept_utf8 and illegal.search(data):
  383. self.syntax_error('illegal character in content')
  384. self.handle_data(data)
  385. self.lineno = self.lineno + data.count('\n')
  386. self.rawdata = rawdata[i+1:]
  387. return self.goahead(end)
  388. self.rawdata = rawdata[i:]
  389. if end:
  390. if not self.__seen_starttag:
  391. self.syntax_error('no elements in file')
  392. if self.stack:
  393. self.syntax_error('missing end tags')
  394. while self.stack:
  395. self.finish_endtag(self.stack[-1][0])
  396. # Internal -- parse comment, return length or -1 if not terminated
  397. def parse_comment(self, i):
  398. rawdata = self.rawdata
  399. if rawdata[i:i+4] != '<!--':
  400. raise Error('unexpected call to handle_comment')
  401. res = commentclose.search(rawdata, i+4)
  402. if res is None:
  403. return -1
  404. if doubledash.search(rawdata, i+4, res.start(0)):
  405. self.syntax_error("`--' inside comment")
  406. if rawdata[res.start(0)-1] == '-':
  407. self.syntax_error('comment cannot end in three dashes')
  408. if not self.__accept_utf8 and \
  409. illegal.search(rawdata, i+4, res.start(0)):
  410. self.syntax_error('illegal character in comment')
  411. self.handle_comment(rawdata[i+4: res.start(0)])
  412. return res.end(0)
  413. # Internal -- handle DOCTYPE tag, return length or -1 if not terminated
  414. def parse_doctype(self, res):
  415. rawdata = self.rawdata
  416. n = len(rawdata)
  417. name = res.group('name')
  418. if self.__map_case:
  419. name = name.lower()
  420. pubid, syslit = res.group('pubid', 'syslit')
  421. if pubid is not None:
  422. pubid = pubid[1:-1] # remove quotes
  423. pubid = ' '.join(pubid.split()) # normalize
  424. if syslit is not None: syslit = syslit[1:-1] # remove quotes
  425. j = k = res.end(0)
  426. if k >= n:
  427. return -1
  428. if rawdata[k] == '[':
  429. level = 0
  430. k = k+1
  431. dq = sq = 0
  432. while k < n:
  433. c = rawdata[k]
  434. if not sq and c == '"':
  435. dq = not dq
  436. elif not dq and c == "'":
  437. sq = not sq
  438. elif sq or dq:
  439. pass
  440. elif level <= 0 and c == ']':
  441. res = endbracket.match(rawdata, k+1)
  442. if res is None:
  443. return -1
  444. self.handle_doctype(name, pubid, syslit, rawdata[j+1:k])
  445. return res.end(0)
  446. elif c == '<':
  447. level = level + 1
  448. elif c == '>':
  449. level = level - 1
  450. if level < 0:
  451. self.syntax_error("bogus `>' in DOCTYPE")
  452. k = k+1
  453. res = endbracketfind.match(rawdata, k)
  454. if res is None:
  455. return -1
  456. if endbracket.match(rawdata, k) is None:
  457. self.syntax_error('garbage in DOCTYPE')
  458. self.handle_doctype(name, pubid, syslit, None)
  459. return res.end(0)
  460. # Internal -- handle CDATA tag, return length or -1 if not terminated
  461. def parse_cdata(self, i):
  462. rawdata = self.rawdata
  463. if rawdata[i:i+9] != '<![CDATA[':
  464. raise Error('unexpected call to parse_cdata')
  465. res = cdataclose.search(rawdata, i+9)
  466. if res is None:
  467. return -1
  468. if not self.__accept_utf8 and \
  469. illegal.search(rawdata, i+9, res.start(0)):
  470. self.syntax_error('illegal character in CDATA')
  471. if not self.stack:
  472. self.syntax_error('CDATA not in content')
  473. self.handle_cdata(rawdata[i+9:res.start(0)])
  474. return res.end(0)
  475. __xml_namespace_attributes = {'ns':None, 'src':None, 'prefix':None}
  476. # Internal -- handle a processing instruction tag
  477. def parse_proc(self, i):
  478. rawdata = self.rawdata
  479. end = procclose.search(rawdata, i)
  480. if end is None:
  481. return -1
  482. j = end.start(0)
  483. if not self.__accept_utf8 and illegal.search(rawdata, i+2, j):
  484. self.syntax_error('illegal character in processing instruction')
  485. res = tagfind.match(rawdata, i+2)
  486. if res is None:
  487. raise Error('unexpected call to parse_proc')
  488. k = res.end(0)
  489. name = res.group(0)
  490. if self.__map_case:
  491. name = name.lower()
  492. if name == 'xml:namespace':
  493. self.syntax_error('old-fashioned namespace declaration')
  494. self.__use_namespaces = -1
  495. # namespace declaration
  496. # this must come after the <?xml?> declaration (if any)
  497. # and before the <!DOCTYPE> (if any).
  498. if self.__seen_doctype or self.__seen_starttag:
  499. self.syntax_error('xml:namespace declaration too late in document')
  500. attrdict, namespace, k = self.parse_attributes(name, k, j)
  501. if namespace:
  502. self.syntax_error('namespace declaration inside namespace declaration')
  503. for attrname in attrdict.keys():
  504. if not attrname in self.__xml_namespace_attributes:
  505. self.syntax_error("unknown attribute `%s' in xml:namespace tag" % attrname)
  506. if not 'ns' in attrdict or not 'prefix' in attrdict:
  507. self.syntax_error('xml:namespace without required attributes')
  508. prefix = attrdict.get('prefix')
  509. if ncname.match(prefix) is None:
  510. self.syntax_error('xml:namespace illegal prefix value')
  511. return end.end(0)
  512. if prefix in self.__namespaces:
  513. self.syntax_error('xml:namespace prefix not unique')
  514. self.__namespaces[prefix] = attrdict['ns']
  515. else:
  516. if name.lower() == 'xml':
  517. self.syntax_error('illegal processing instruction target name')
  518. self.handle_proc(name, rawdata[k:j])
  519. return end.end(0)
  520. # Internal -- parse attributes between i and j
  521. def parse_attributes(self, tag, i, j):
  522. rawdata = self.rawdata
  523. attrdict = {}
  524. namespace = {}
  525. while i < j:
  526. res = attrfind.match(rawdata, i)
  527. if res is None:
  528. break
  529. attrname, attrvalue = res.group('name', 'value')
  530. if self.__map_case:
  531. attrname = attrname.lower()
  532. i = res.end(0)
  533. if attrvalue is None:
  534. self.syntax_error("no value specified for attribute `%s'" % attrname)
  535. attrvalue = attrname
  536. elif attrvalue[:1] == "'" == attrvalue[-1:] or \
  537. attrvalue[:1] == '"' == attrvalue[-1:]:
  538. attrvalue = attrvalue[1:-1]
  539. elif not self.__accept_unquoted_attributes:
  540. self.syntax_error("attribute `%s' value not quoted" % attrname)
  541. res = xmlns.match(attrname)
  542. if res is not None:
  543. # namespace declaration
  544. ncname = res.group('ncname')
  545. namespace[ncname or ''] = attrvalue or None
  546. if not self.__use_namespaces:
  547. self.__use_namespaces = len(self.stack)+1
  548. continue
  549. if '<' in attrvalue:
  550. self.syntax_error("`<' illegal in attribute value")
  551. if attrname in attrdict:
  552. self.syntax_error("attribute `%s' specified twice" % attrname)
  553. attrvalue = attrvalue.translate(attrtrans)
  554. attrdict[attrname] = self.translate_references(attrvalue)
  555. return attrdict, namespace, i
  556. # Internal -- handle starttag, return length or -1 if not terminated
  557. def parse_starttag(self, i):
  558. rawdata = self.rawdata
  559. # i points to start of tag
  560. end = endbracketfind.match(rawdata, i+1)
  561. if end is None:
  562. return -1
  563. tag = starttagmatch.match(rawdata, i)
  564. if tag is None or tag.end(0) != end.end(0):
  565. self.syntax_error('garbage in starttag')
  566. return end.end(0)
  567. nstag = tagname = tag.group('tagname')
  568. if self.__map_case:
  569. nstag = tagname = nstag.lower()
  570. if not self.__seen_starttag and self.__seen_doctype and \
  571. tagname != self.__seen_doctype:
  572. self.syntax_error('starttag does not match DOCTYPE')
  573. if self.__seen_starttag and not self.stack:
  574. self.syntax_error('multiple elements on top level')
  575. k, j = tag.span('attrs')
  576. attrdict, nsdict, k = self.parse_attributes(tagname, k, j)
  577. self.stack.append((tagname, nsdict, nstag))
  578. if self.__use_namespaces:
  579. res = qname.match(tagname)
  580. else:
  581. res = None
  582. if res is not None:
  583. prefix, nstag = res.group('prefix', 'local')
  584. if prefix is None:
  585. prefix = ''
  586. ns = None
  587. for t, d, nst in self.stack:
  588. if prefix in d:
  589. ns = d[prefix]
  590. if ns is None and prefix != '':
  591. ns = self.__namespaces.get(prefix)
  592. if ns is not None:
  593. nstag = ns + ' ' + nstag
  594. elif prefix != '':
  595. nstag = prefix + ':' + nstag # undo split
  596. self.stack[-1] = tagname, nsdict, nstag
  597. # translate namespace of attributes
  598. attrnamemap = {} # map from new name to old name (used for error reporting)
  599. for key in attrdict.keys():
  600. attrnamemap[key] = key
  601. if self.__use_namespaces:
  602. nattrdict = {}
  603. for key, val in attrdict.items():
  604. okey = key
  605. res = qname.match(key)
  606. if res is not None:
  607. aprefix, key = res.group('prefix', 'local')
  608. if self.__map_case:
  609. key = key.lower()
  610. if aprefix is not None:
  611. ans = None
  612. for t, d, nst in self.stack:
  613. if aprefix in d:
  614. ans = d[aprefix]
  615. if ans is None:
  616. ans = self.__namespaces.get(aprefix)
  617. if ans is not None:
  618. key = ans + ' ' + key
  619. else:
  620. key = aprefix + ':' + key
  621. nattrdict[key] = val
  622. attrnamemap[key] = okey
  623. attrdict = nattrdict
  624. attributes = self.attributes.get(nstag)
  625. if attributes is not None:
  626. for key in attrdict.keys():
  627. if not key in attributes:
  628. self.syntax_error("unknown attribute `%s' in tag `%s'" % (attrnamemap[key], tagname))
  629. for key, val in attributes.items():
  630. if val is not None and not key in attrdict:
  631. attrdict[key] = val
  632. method = self.elements.get(nstag, (None, None))[0]
  633. self.finish_starttag(nstag, attrdict, method)
  634. if tag.group('slash') == '/':
  635. self.finish_endtag(tagname)
  636. return tag.end(0)
  637. # Internal -- parse endtag
  638. def parse_endtag(self, i):
  639. rawdata = self.rawdata
  640. end = endbracketfind.match(rawdata, i+1)
  641. if end is None:
  642. return -1
  643. res = tagfind.match(rawdata, i+2)
  644. if res is None:
  645. if self.literal:
  646. self.handle_data(rawdata[i])
  647. return i+1
  648. if not self.__accept_missing_endtag_name:
  649. self.syntax_error('no name specified in end tag')
  650. tag = self.stack[-1][0]
  651. k = i+2
  652. else:
  653. tag = res.group(0)
  654. if self.__map_case:
  655. tag = tag.lower()
  656. if self.literal:
  657. if not self.stack or tag != self.stack[-1][0]:
  658. self.handle_data(rawdata[i])
  659. return i+1
  660. k = res.end(0)
  661. if endbracket.match(rawdata, k) is None:
  662. self.syntax_error('garbage in end tag')
  663. self.finish_endtag(tag)
  664. return end.end(0)
  665. # Internal -- finish processing of start tag
  666. def finish_starttag(self, tagname, attrdict, method):
  667. if method is not None:
  668. self.handle_starttag(tagname, method, attrdict)
  669. else:
  670. self.unknown_starttag(tagname, attrdict)
  671. # Internal -- finish processing of end tag
  672. def finish_endtag(self, tag):
  673. self.literal = 0
  674. if not tag:
  675. self.syntax_error('name-less end tag')
  676. found = len(self.stack) - 1
  677. if found < 0:
  678. self.unknown_endtag(tag)
  679. return
  680. else:
  681. found = -1
  682. for i in range(len(self.stack)):
  683. if tag == self.stack[i][0]:
  684. found = i
  685. if found == -1:
  686. self.syntax_error('unopened end tag')
  687. return
  688. while len(self.stack) > found:
  689. if found < len(self.stack) - 1:
  690. self.syntax_error('missing close tag for %s' % self.stack[-1][2])
  691. nstag = self.stack[-1][2]
  692. method = self.elements.get(nstag, (None, None))[1]
  693. if method is not None:
  694. self.handle_endtag(nstag, method)
  695. else:
  696. self.unknown_endtag(nstag)
  697. if self.__use_namespaces == len(self.stack):
  698. self.__use_namespaces = 0
  699. del self.stack[-1]
  700. # Overridable -- handle xml processing instruction
  701. def handle_xml(self, encoding, standalone):
  702. pass
  703. # Overridable -- handle DOCTYPE
  704. def handle_doctype(self, tag, pubid, syslit, data):
  705. pass
  706. # Overridable -- handle start tag
  707. def handle_starttag(self, tag, method, attrs):
  708. method(attrs)
  709. # Overridable -- handle end tag
  710. def handle_endtag(self, tag, method):
  711. method()
  712. # Example -- handle character reference, no need to override
  713. def handle_charref(self, name):
  714. try:
  715. if name[0] == 'x':
  716. n = int(name[1:], 16)
  717. else:
  718. n = int(name)
  719. except ValueError:
  720. self.unknown_charref(name)
  721. return
  722. if not 0 <= n <= 255:
  723. self.unknown_charref(name)
  724. return
  725. self.handle_data(chr(n))
  726. # Definition of entities -- derived classes may override
  727. entitydefs = {'lt': '&#60;', # must use charref
  728. 'gt': '&#62;',
  729. 'amp': '&#38;', # must use charref
  730. 'quot': '&#34;',
  731. 'apos': '&#39;',
  732. }
  733. # Example -- handle data, should be overridden
  734. def handle_data(self, data):
  735. pass
  736. # Example -- handle cdata, could be overridden
  737. def handle_cdata(self, data):
  738. pass
  739. # Example -- handle comment, could be overridden
  740. def handle_comment(self, data):
  741. pass
  742. # Example -- handle processing instructions, could be overridden
  743. def handle_proc(self, name, data):
  744. pass
  745. # Example -- handle relatively harmless syntax errors, could be overridden
  746. def syntax_error(self, message):
  747. raise Error('Syntax error at line %d: %s' % (self.lineno, message))
  748. # To be overridden -- handlers for unknown objects
  749. def unknown_starttag(self, tag, attrs): pass
  750. def unknown_endtag(self, tag): pass
  751. def unknown_charref(self, ref): pass
  752. def unknown_entityref(self, name):
  753. self.syntax_error("reference to unknown entity `&%s;'" % name)
  754. class TestXMLParser(XMLParser):
  755. def __init__(self, **kw):
  756. self.testdata = ""
  757. XMLParser.__init__(self, **kw)
  758. def handle_xml(self, encoding, standalone):
  759. self.flush()
  760. print 'xml: encoding =',encoding,'standalone =',standalone
  761. def handle_doctype(self, tag, pubid, syslit, data):
  762. self.flush()
  763. print 'DOCTYPE:',tag, repr(data)
  764. def handle_data(self, data):
  765. self.testdata = self.testdata + data
  766. if len(repr(self.testdata)) >= 70:
  767. self.flush()
  768. def flush(self):
  769. data = self.testdata
  770. if data:
  771. self.testdata = ""
  772. print 'data:', repr(data)
  773. def handle_cdata(self, data):
  774. self.flush()
  775. print 'cdata:', repr(data)
  776. def handle_proc(self, name, data):
  777. self.flush()
  778. print 'processing:',name,repr(data)
  779. def handle_comment(self, data):
  780. self.flush()
  781. r = repr(data)
  782. if len(r) > 68:
  783. r = r[:32] + '...' + r[-32:]
  784. print 'comment:', r
  785. def syntax_error(self, message):
  786. print 'error at line %d:' % self.lineno, message
  787. def unknown_starttag(self, tag, attrs):
  788. self.flush()
  789. if not attrs:
  790. print 'start tag: <' + tag + '>'
  791. else:
  792. print 'start tag: <' + tag,
  793. for name, value in attrs.items():
  794. print name + '=' + '"' + value + '"',
  795. print '>'
  796. def unknown_endtag(self, tag):
  797. self.flush()
  798. print 'end tag: </' + tag + '>'
  799. def unknown_entityref(self, ref):
  800. self.flush()
  801. print '*** unknown entity ref: &' + ref + ';'
  802. def unknown_charref(self, ref):
  803. self.flush()
  804. print '*** unknown char ref: &#' + ref + ';'
  805. def close(self):
  806. XMLParser.close(self)
  807. self.flush()
  808. def test(args = None):
  809. import sys, getopt
  810. from time import time
  811. if not args:
  812. args = sys.argv[1:]
  813. opts, args = getopt.getopt(args, 'st')
  814. klass = TestXMLParser
  815. do_time = 0
  816. for o, a in opts:
  817. if o == '-s':
  818. klass = XMLParser
  819. elif o == '-t':
  820. do_time = 1
  821. if args:
  822. file = args[0]
  823. else:
  824. file = 'test.xml'
  825. if file == '-':
  826. f = sys.stdin
  827. else:
  828. try:
  829. f = open(file, 'r')
  830. except IOError, msg:
  831. print file, ":", msg
  832. sys.exit(1)
  833. data = f.read()
  834. if f is not sys.stdin:
  835. f.close()
  836. x = klass()
  837. t0 = time()
  838. try:
  839. if do_time:
  840. x.feed(data)
  841. x.close()
  842. else:
  843. for c in data:
  844. x.feed(c)
  845. x.close()
  846. except Error, msg:
  847. t1 = time()
  848. print msg
  849. if do_time:
  850. print 'total time: %g' % (t1-t0)
  851. sys.exit(1)
  852. t1 = time()
  853. if do_time:
  854. print 'total time: %g' % (t1-t0)
  855. if __name__ == '__main__':
  856. test()