Parser/Composer library for Python
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
5.4 KiB

  1. """
  2. XML AST generator
  3. pyPEG parsing framework
  4. Copyleft 2012, Volker Birk.
  5. This program is under GNU General Public License 2.0.
  6. """
  7. from __future__ import unicode_literals
  8. try:
  9. str = unicode
  10. except NameError:
  11. pass
  12. __version__ = 2.50
  13. __author__ = "Volker Birk"
  14. __license__ = "This program is under GNU General Public License 2.0."
  15. __url__ = "http://fdik.org/pyPEG"
  16. try:
  17. import lxml
  18. from lxml import etree
  19. except ImportError:
  20. import xml.etree.ElementTree as etree
  21. if __debug__:
  22. import warnings
  23. import pypeg2
  24. def create_tree(thing, parent=None, object_names=False):
  25. """Create an XML etree from a thing.
  26. Arguments:
  27. thing thing to interpret
  28. parent etree.Element to put subtree into
  29. default: create a new Element tree
  30. object_names experimental feature: if True tag names are object
  31. names instead of types
  32. Returns:
  33. etree.Element instance created
  34. """
  35. try:
  36. grammar = type(thing).grammar
  37. except AttributeError:
  38. if isinstance(thing, list):
  39. grammar = pypeg2.csl(pypeg2.name())
  40. else:
  41. grammar = pypeg2.word
  42. name = type(thing).__name__
  43. if object_names:
  44. try:
  45. name = str(thing.name)
  46. name = name.replace(" ", "_")
  47. except AttributeError:
  48. pass
  49. if parent is None:
  50. me = etree.Element(name)
  51. else:
  52. me = etree.SubElement(parent, name)
  53. for e in pypeg2.attributes(grammar):
  54. if object_names and e.name == "name":
  55. if name != type(thing).__name__:
  56. continue
  57. key, value = e.name, getattr(thing, e.name, None)
  58. if value is not None:
  59. if pypeg2._issubclass(e.thing, (str, int, pypeg2.Literal)) \
  60. or type(e.thing) == pypeg2._RegEx:
  61. me.set(key, str(value))
  62. else:
  63. create_tree(value, me, object_names)
  64. if isinstance(thing, list):
  65. things = thing
  66. elif isinstance(thing, pypeg2.Namespace):
  67. things = thing.values()
  68. else:
  69. things = []
  70. last = None
  71. for t in things:
  72. if type(t) == str:
  73. if last is not None:
  74. last.tail = str(t)
  75. else:
  76. me.text = str(t)
  77. else:
  78. last = create_tree(t, me, object_names)
  79. if isinstance(thing, str):
  80. me.text = str(thing)
  81. return me
  82. def thing2xml(thing, pretty=False, object_names=False):
  83. """Create XML text from a thing.
  84. Arguments:
  85. thing thing to interpret
  86. pretty True if XML should be indented
  87. False if XML should be plain
  88. object_names experimental feature: if True tag names are object
  89. names instead of types
  90. Returns:
  91. bytes with encoded XML
  92. """
  93. tree = create_tree(thing, None, object_names)
  94. try:
  95. if lxml:
  96. txt = etree.tostring(tree, pretty_print=pretty)
  97. except NameError:
  98. if __debug__:
  99. if pretty:
  100. warnings.warn("lxml is needed for pretty printing",
  101. ImportWarning)
  102. etree.indent(tree)
  103. txt = etree.tostring(tree)
  104. return txt
  105. def create_thing(element, symbol_table):
  106. """Create thing from an XML element.
  107. Arguments:
  108. element etree.Element instance to read
  109. symbol_table symbol table where the classes can be found
  110. Returns:
  111. thing created
  112. """
  113. C = symbol_table[element.tag]
  114. if element.text:
  115. thing = C(element.text)
  116. else:
  117. thing = C()
  118. subs = iter(element)
  119. iterated_already = False
  120. try:
  121. grammar = C.grammar
  122. except AttributeError:
  123. pass
  124. else:
  125. for e in pypeg2.attributes(grammar):
  126. key = e.name
  127. if pypeg2._issubclass(e.thing, (str, int, pypeg2.Literal)) \
  128. or type(e.thing) == pypeg2._RegEx:
  129. try:
  130. value = element.attrib[e.name]
  131. except KeyError:
  132. pass
  133. else:
  134. setattr(thing, key, e.thing(value))
  135. else:
  136. try:
  137. if not iterated_already:
  138. iterated_already = True
  139. sub = next(subs)
  140. except StopIteration:
  141. pass
  142. if sub.tag == e.thing.__name__:
  143. iterated_already = False
  144. t = create_thing(sub, symbol_table)
  145. setattr(thing, key, t)
  146. if issubclass(C, list) or issubclass(C, pypeg2.Namespace):
  147. try:
  148. while True:
  149. if iterated_already:
  150. iterated_alread = False
  151. else:
  152. sub = next(subs)
  153. t = create_thing(sub, symbol_table)
  154. if isinstance(thing, pypeg2.List):
  155. thing.append(t)
  156. else:
  157. thing[t.name] = t
  158. except StopIteration:
  159. pass
  160. return thing
  161. def xml2thing(xml, symbol_table):
  162. """Create thing from XML text.
  163. Arguments:
  164. xml bytes with encoded XML
  165. symbol_table symbol table where the classes can be found
  166. Returns:
  167. created thing
  168. """
  169. element = etree.fromstring(xml)
  170. return create_thing(element, symbol_table)