1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
"""This file contains a simple parser for the XML output of GLADE.
It is not a true XML parser, since it requires tags to be of the form
<tag>
<tag2>data</tag2>
</tag>
That is tags with embeded tags in them must have the opening tag on a single
line, and the closing tag on another line by itself. Tags with no embedded
tags should have openning tag, data and closing tag all on one line. Also
tag attributes are not supported. Yes I know this is a bit lame, but it is
the minimum required for reading GLADE output.
This module is not really glade specific, except that it can read GLADE
output (it can probably also read some other types of XML documents)
You should call one of read(fname), read_stream(fp) or read_string(str).
The output is a tree of TagTree. Tags of a node can be accessed either as
attributes (eg node.tag) or as list items (eg node['tag']). If there was
more than one of that tag name at this level, they will be returned as a
tuple by the previous two methods.
"""
import string
import regex
# StringIO module for parsing an XML document stored in a string. We pick
# the faster cStringIO implementation if present.
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
error = "pyglade.xmlparse.error"
comment_line = regex.compile('<\\?.*>')
# data_tag must be checked first, since open_tag and close_tag will match
# the same strings data_tag matches.
data_tag = regex.compile('<\([^>]+\)>\(.*\)</\([^>]+\)>')
open_tag = regex.compile('<\([^/>][^>]*\)>')
close_tag = regex.compile('</\([^>]+\)>')
class TagTree:
def __init__(self, parent, tag):
self.parent = parent
self.tag = tag
self.__tags = {}
def __getitem__(self, key):
return self.__tags[key]
__getattr__ = __getitem__
def __setitem__(self, key, value):
self.__tags[key] = value
def __len__(self):
return len(self.__tags)
def has_key(self, key):
return self.__tags.has_key(key)
def keys(self):
return self.__tags.keys()
def get(self, key, default):
if self.__tags.has_key(key):
return self.__tags[key]
else:
return default
def get_bool(self, key, default=0):
if self.__tags.has_key(key):
return string.lower(self.__tags[key]) == 'true'
else:
return default
def get_int(self, key, default=0):
if self.__tags.has_key(key):
return string.atoi(self.__tags[key])
else:
return default
def get_float(self, key, default=0.0):
if self.__tags.has_key(key):
return string.atof(self.__tags[key])
else:
return default
def destroy(self):
# This is required to break a dependency loop
del self.parent
for key in self.__tags.keys():
vals = self.__tags[key]
if type(vals) != type(()): vals = (vals,)
for val in vals:
if hasattr(val, 'destroy'): val.destroy()
del self.__tags[key]
def read_stream(fp):
base = TagTree(parent=None, tag='XML-Base')
cstack = [base]
line = fp.readline()
while line:
if comment_line.search(line) >= 0 or line == '\n':
pass
elif data_tag.search(line) >= 0:
key = string.lower(data_tag.group(1))
data = data_tag.group(2)
end = string.lower(data_tag.group(3))
if key != end:
raise error, "unmatching tags: %s and %s" % \
(key, end)
if cstack[-1].has_key(key):
oldval = cstack[-1][key]
if type(oldval) == type(()):
cstack[-1][key] = oldval + (data,)
else:
cstack[-1][key] = (oldval, data)
else:
cstack[-1][key] = data
elif open_tag.search(line) >= 0:
key = string.lower(open_tag.group(1))
tree = TagTree(parent=cstack[-1], tag=key)
if cstack[-1].has_key(key):
oldval = cstack[-1][key]
if type(oldval) == type(()):
cstack[-1][key] = oldval + (tree,)
else:
cstack[-1][key] = (oldval, tree)
else:
cstack[-1][key] = tree
cstack.append(tree)
elif close_tag.search(line) >= 0:
key = string.lower(close_tag.group(1))
if not cstack:
raise error, "no tags to match " + key
if key != cstack[-1].tag:
raise error, \
"unmatching container tags: %s and %s" %\
(cstack[-1].type, key)
del cstack[-1]
else:
raise error, "unparseable line: " + line
line = fp.readline()
if len(cstack) != 1:
raise error, "some unclosed tags are present"
return base
def read(fname):
return read_stream(open(fname, "r"))
def read_string(string):
return read_stream(StringIO(string))
|