blob: 0b1748cd481e04aac5d95ce7e4ea1d1b35223cac [file] [log] [blame]
Georg Brandld4315192009-04-05 15:14:29 +00001r"""plistlib.py -- a tool to generate and parse MacOSX .plist files.
Just van Rossum0ec27442002-11-19 22:01:02 +00002
Georg Brandl864de822008-01-21 16:34:07 +00003The PropertyList (.plist) file format is a simple XML pickle supporting
Just van Rossum4c3d0542004-10-02 08:40:47 +00004basic object types, like dictionaries, lists, numbers and strings.
5Usually the top level object is a dictionary.
Just van Rossum0ec27442002-11-19 22:01:02 +00006
Just van Rossum4c3d0542004-10-02 08:40:47 +00007To write out a plist file, use the writePlist(rootObject, pathOrFile)
8function. 'rootObject' is the top level object, 'pathOrFile' is a
9filename or a (writable) file object.
Just van Rossum0ec27442002-11-19 22:01:02 +000010
Just van Rossumc6fdd1b2004-10-26 06:50:50 +000011To parse a plist from a file, use the readPlist(pathOrFile) function,
12with a file name or a (readable) file object as the only argument. It
13returns the top level object (again, usually a dictionary).
14
15To work with plist data in strings, you can use readPlistFromString()
16and writePlistToString().
Just van Rossum0ec27442002-11-19 22:01:02 +000017
18Values can be strings, integers, floats, booleans, tuples, lists,
Just van Rossum7c944872004-10-26 07:20:26 +000019dictionaries, Data or datetime.datetime objects. String values (including
20dictionary keys) may be unicode strings -- they will be written out as
21UTF-8.
Just van Rossum0ec27442002-11-19 22:01:02 +000022
Just van Rossum0ec27442002-11-19 22:01:02 +000023The <data> plist type is supported through the Data class. This is a
24thin wrapper around a Python string.
25
Just van Rossum0ec27442002-11-19 22:01:02 +000026Generate Plist example:
27
Just van Rossum368c0b22004-10-26 07:38:16 +000028 pl = dict(
Jack Jansen0ae32202003-04-09 13:25:43 +000029 aString="Doodah",
30 aList=["A", "B", 12, 32.1, [1, 2, 3]],
Walter Dörwald4a11a062008-01-21 20:18:04 +000031 aFloat=0.1,
32 anInt=728,
Just van Rossum368c0b22004-10-26 07:38:16 +000033 aDict=dict(
Jack Jansen0ae32202003-04-09 13:25:43 +000034 anotherString="<hello & hi there!>",
35 aUnicodeValue=u'M\xe4ssig, Ma\xdf',
36 aTrueValue=True,
37 aFalseValue=False,
38 ),
Walter Dörwald4a11a062008-01-21 20:18:04 +000039 someData=Data("<binary gunk>"),
40 someMoreData=Data("<lots of binary gunk>" * 10),
41 aDate=datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
Jack Jansen0ae32202003-04-09 13:25:43 +000042 )
43 # unicode keys are possible, but a little awkward to use:
44 pl[u'\xc5benraa'] = "That was a unicode key."
Just van Rossum4c3d0542004-10-02 08:40:47 +000045 writePlist(pl, fileName)
Just van Rossum0ec27442002-11-19 22:01:02 +000046
47Parse Plist example:
48
Just van Rossum4c3d0542004-10-02 08:40:47 +000049 pl = readPlist(pathOrFile)
Just van Rossum368c0b22004-10-26 07:38:16 +000050 print pl["aKey"]
Just van Rossum0ec27442002-11-19 22:01:02 +000051"""
52
Just van Rossum4c3d0542004-10-02 08:40:47 +000053
Just van Rossum95387a12004-10-25 15:10:42 +000054__all__ = [
Just van Rossumc6fdd1b2004-10-26 06:50:50 +000055 "readPlist", "writePlist", "readPlistFromString", "writePlistToString",
Just van Rossum95387a12004-10-25 15:10:42 +000056 "readPlistFromResource", "writePlistToResource",
Just van Rossum7c944872004-10-26 07:20:26 +000057 "Plist", "Data", "Dict"
Just van Rossum95387a12004-10-25 15:10:42 +000058]
Just van Rossum368c0b22004-10-26 07:38:16 +000059# Note: the Plist and Dict classes have been deprecated.
Just van Rossum0ec27442002-11-19 22:01:02 +000060
Just van Rossum1f74ef02004-10-26 10:30:55 +000061import binascii
Just van Rossumc6fdd1b2004-10-26 06:50:50 +000062import datetime
63from cStringIO import StringIO
Just van Rossum26e811a2004-11-12 08:02:35 +000064import re
Benjamin Peterson23681932008-05-12 21:42:13 +000065import warnings
Just van Rossum95387a12004-10-25 15:10:42 +000066
Just van Rossum0ec27442002-11-19 22:01:02 +000067
Just van Rossum4c3d0542004-10-02 08:40:47 +000068def readPlist(pathOrFile):
69 """Read a .plist file. 'pathOrFile' may either be a file name or a
70 (readable) file object. Return the unpacked root object (which
71 usually is a dictionary).
72 """
73 didOpen = 0
74 if isinstance(pathOrFile, (str, unicode)):
75 pathOrFile = open(pathOrFile)
76 didOpen = 1
77 p = PlistParser()
78 rootObject = p.parse(pathOrFile)
79 if didOpen:
80 pathOrFile.close()
81 return rootObject
Just van Rossum0ec27442002-11-19 22:01:02 +000082
83
Just van Rossum4c3d0542004-10-02 08:40:47 +000084def writePlist(rootObject, pathOrFile):
85 """Write 'rootObject' to a .plist file. 'pathOrFile' may either be a
86 file name or a (writable) file object.
87 """
88 didOpen = 0
89 if isinstance(pathOrFile, (str, unicode)):
90 pathOrFile = open(pathOrFile, "w")
91 didOpen = 1
92 writer = PlistWriter(pathOrFile)
93 writer.writeln("<plist version=\"1.0\">")
94 writer.writeValue(rootObject)
95 writer.writeln("</plist>")
96 if didOpen:
97 pathOrFile.close()
Just van Rossum0ec27442002-11-19 22:01:02 +000098
99
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000100def readPlistFromString(data):
101 """Read a plist data from a string. Return the root object.
102 """
103 return readPlist(StringIO(data))
104
105
106def writePlistToString(rootObject):
107 """Return 'rootObject' as a plist-formatted string.
108 """
109 f = StringIO()
110 writePlist(rootObject, f)
111 return f.getvalue()
112
113
Just van Rossum95387a12004-10-25 15:10:42 +0000114def readPlistFromResource(path, restype='plst', resid=0):
115 """Read plst resource from the resource fork of path.
116 """
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000117 warnings.warnpy3k("In 3.x, readPlistFromResource is removed.",
118 stacklevel=2)
Just van Rossum95387a12004-10-25 15:10:42 +0000119 from Carbon.File import FSRef, FSGetResourceForkName
120 from Carbon.Files import fsRdPerm
121 from Carbon import Res
Just van Rossum95387a12004-10-25 15:10:42 +0000122 fsRef = FSRef(path)
123 resNum = Res.FSOpenResourceFile(fsRef, FSGetResourceForkName(), fsRdPerm)
124 Res.UseResFile(resNum)
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000125 plistData = Res.Get1Resource(restype, resid).data
Just van Rossum95387a12004-10-25 15:10:42 +0000126 Res.CloseResFile(resNum)
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000127 return readPlistFromString(plistData)
Just van Rossum95387a12004-10-25 15:10:42 +0000128
129
130def writePlistToResource(rootObject, path, restype='plst', resid=0):
131 """Write 'rootObject' as a plst resource to the resource fork of path.
132 """
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000133 warnings.warnpy3k("In 3.x, writePlistToResource is removed.", stacklevel=2)
Just van Rossum95387a12004-10-25 15:10:42 +0000134 from Carbon.File import FSRef, FSGetResourceForkName
135 from Carbon.Files import fsRdWrPerm
136 from Carbon import Res
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000137 plistData = writePlistToString(rootObject)
Just van Rossum95387a12004-10-25 15:10:42 +0000138 fsRef = FSRef(path)
139 resNum = Res.FSOpenResourceFile(fsRef, FSGetResourceForkName(), fsRdWrPerm)
140 Res.UseResFile(resNum)
141 try:
142 Res.Get1Resource(restype, resid).RemoveResource()
143 except Res.Error:
144 pass
145 res = Res.Resource(plistData)
146 res.AddResource(restype, resid, '')
147 res.WriteResource()
148 Res.CloseResFile(resNum)
149
150
Just van Rossum0ec27442002-11-19 22:01:02 +0000151class DumbXMLWriter:
152
Just van Rossum4c3d0542004-10-02 08:40:47 +0000153 def __init__(self, file, indentLevel=0, indent="\t"):
Jack Jansen0ae32202003-04-09 13:25:43 +0000154 self.file = file
155 self.stack = []
Just van Rossum4c3d0542004-10-02 08:40:47 +0000156 self.indentLevel = indentLevel
157 self.indent = indent
Just van Rossum0ec27442002-11-19 22:01:02 +0000158
Jack Jansen0ae32202003-04-09 13:25:43 +0000159 def beginElement(self, element):
160 self.stack.append(element)
161 self.writeln("<%s>" % element)
162 self.indentLevel += 1
Just van Rossum0ec27442002-11-19 22:01:02 +0000163
Jack Jansen0ae32202003-04-09 13:25:43 +0000164 def endElement(self, element):
165 assert self.indentLevel > 0
166 assert self.stack.pop() == element
167 self.indentLevel -= 1
168 self.writeln("</%s>" % element)
Just van Rossum0ec27442002-11-19 22:01:02 +0000169
Jack Jansen0ae32202003-04-09 13:25:43 +0000170 def simpleElement(self, element, value=None):
Just van Rossum87316ec2003-07-10 14:26:06 +0000171 if value is not None:
Just van Rossum4c3d0542004-10-02 08:40:47 +0000172 value = _escapeAndEncode(value)
Jack Jansen0ae32202003-04-09 13:25:43 +0000173 self.writeln("<%s>%s</%s>" % (element, value, element))
174 else:
175 self.writeln("<%s/>" % element)
Just van Rossum0ec27442002-11-19 22:01:02 +0000176
Jack Jansen0ae32202003-04-09 13:25:43 +0000177 def writeln(self, line):
178 if line:
Just van Rossum4c3d0542004-10-02 08:40:47 +0000179 self.file.write(self.indentLevel * self.indent + line + "\n")
Jack Jansen0ae32202003-04-09 13:25:43 +0000180 else:
181 self.file.write("\n")
Just van Rossum0ec27442002-11-19 22:01:02 +0000182
183
Just van Rossum95387a12004-10-25 15:10:42 +0000184# Contents should conform to a subset of ISO 8601
185# (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units may be omitted with
186# a loss of precision)
187_dateParser = re.compile(r"(?P<year>\d\d\d\d)(?:-(?P<month>\d\d)(?:-(?P<day>\d\d)(?:T(?P<hour>\d\d)(?::(?P<minute>\d\d)(?::(?P<second>\d\d))?)?)?)?)?Z")
188
Just van Rossum7c944872004-10-26 07:20:26 +0000189def _dateFromString(s):
190 order = ('year', 'month', 'day', 'hour', 'minute', 'second')
191 gd = _dateParser.match(s).groupdict()
192 lst = []
193 for key in order:
194 val = gd[key]
195 if val is None:
196 break
197 lst.append(int(val))
198 return datetime.datetime(*lst)
199
200def _dateToString(d):
201 return '%04d-%02d-%02dT%02d:%02d:%02dZ' % (
202 d.year, d.month, d.day,
203 d.hour, d.minute, d.second
204 )
205
Just van Rossum26e811a2004-11-12 08:02:35 +0000206
Just van Rossum2dae7642004-11-12 09:36:12 +0000207# Regex to find any control chars, except for \t \n and \r
208_controlCharPat = re.compile(
Just van Rossumb84330d2004-11-12 08:14:49 +0000209 r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
210 r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]")
Just van Rossum26e811a2004-11-12 08:02:35 +0000211
Just van Rossum4c3d0542004-10-02 08:40:47 +0000212def _escapeAndEncode(text):
Just van Rossum2dae7642004-11-12 09:36:12 +0000213 m = _controlCharPat.search(text)
214 if m is not None:
215 raise ValueError("strings can't contains control characters; "
216 "use plistlib.Data instead")
Just van Rossum4c3d0542004-10-02 08:40:47 +0000217 text = text.replace("\r\n", "\n") # convert DOS line endings
218 text = text.replace("\r", "\n") # convert Mac line endings
219 text = text.replace("&", "&amp;") # escape '&'
220 text = text.replace("<", "&lt;") # escape '<'
Just van Rossum8b8dece2004-10-26 10:11:00 +0000221 text = text.replace(">", "&gt;") # escape '>'
Just van Rossum4c3d0542004-10-02 08:40:47 +0000222 return text.encode("utf-8") # encode as UTF-8
Just van Rossum0ec27442002-11-19 22:01:02 +0000223
224
225PLISTHEADER = """\
226<?xml version="1.0" encoding="UTF-8"?>
227<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
228"""
229
230class PlistWriter(DumbXMLWriter):
231
Just van Rossum4c3d0542004-10-02 08:40:47 +0000232 def __init__(self, file, indentLevel=0, indent="\t", writeHeader=1):
233 if writeHeader:
234 file.write(PLISTHEADER)
235 DumbXMLWriter.__init__(self, file, indentLevel, indent)
Just van Rossum0ec27442002-11-19 22:01:02 +0000236
Jack Jansen0ae32202003-04-09 13:25:43 +0000237 def writeValue(self, value):
238 if isinstance(value, (str, unicode)):
239 self.simpleElement("string", value)
240 elif isinstance(value, bool):
241 # must switch for bool before int, as bool is a
242 # subclass of int...
243 if value:
244 self.simpleElement("true")
245 else:
246 self.simpleElement("false")
Christian Heimes06131882008-01-04 00:04:52 +0000247 elif isinstance(value, (int, long)):
248 self.simpleElement("integer", "%d" % value)
Jack Jansen0ae32202003-04-09 13:25:43 +0000249 elif isinstance(value, float):
Just van Rossum95387a12004-10-25 15:10:42 +0000250 self.simpleElement("real", repr(value))
Just van Rossum4c3d0542004-10-02 08:40:47 +0000251 elif isinstance(value, dict):
Jack Jansen0ae32202003-04-09 13:25:43 +0000252 self.writeDict(value)
253 elif isinstance(value, Data):
254 self.writeData(value)
Just van Rossum7c944872004-10-26 07:20:26 +0000255 elif isinstance(value, datetime.datetime):
256 self.simpleElement("date", _dateToString(value))
Jack Jansen0ae32202003-04-09 13:25:43 +0000257 elif isinstance(value, (tuple, list)):
258 self.writeArray(value)
259 else:
Just van Rossum94af32e2003-07-01 20:15:38 +0000260 raise TypeError("unsuported type: %s" % type(value))
Just van Rossum0ec27442002-11-19 22:01:02 +0000261
Jack Jansen0ae32202003-04-09 13:25:43 +0000262 def writeData(self, data):
263 self.beginElement("data")
Just van Rossum1f74ef02004-10-26 10:30:55 +0000264 self.indentLevel -= 1
265 maxlinelength = 76 - len(self.indent.replace("\t", " " * 8) *
266 self.indentLevel)
267 for line in data.asBase64(maxlinelength).split("\n"):
Jack Jansen0ae32202003-04-09 13:25:43 +0000268 if line:
269 self.writeln(line)
Just van Rossum1f74ef02004-10-26 10:30:55 +0000270 self.indentLevel += 1
Jack Jansen0ae32202003-04-09 13:25:43 +0000271 self.endElement("data")
Just van Rossum0ec27442002-11-19 22:01:02 +0000272
Jack Jansen0ae32202003-04-09 13:25:43 +0000273 def writeDict(self, d):
274 self.beginElement("dict")
275 items = d.items()
276 items.sort()
277 for key, value in items:
Just van Rossum94af32e2003-07-01 20:15:38 +0000278 if not isinstance(key, (str, unicode)):
279 raise TypeError("keys must be strings")
Jack Jansen0ae32202003-04-09 13:25:43 +0000280 self.simpleElement("key", key)
281 self.writeValue(value)
282 self.endElement("dict")
Just van Rossum0ec27442002-11-19 22:01:02 +0000283
Jack Jansen0ae32202003-04-09 13:25:43 +0000284 def writeArray(self, array):
285 self.beginElement("array")
286 for value in array:
287 self.writeValue(value)
288 self.endElement("array")
Just van Rossum0ec27442002-11-19 22:01:02 +0000289
290
Just van Rossum368c0b22004-10-26 07:38:16 +0000291class _InternalDict(dict):
Just van Rossum0ec27442002-11-19 22:01:02 +0000292
Just van Rossum368c0b22004-10-26 07:38:16 +0000293 # This class is needed while Dict is scheduled for deprecation:
294 # we only need to warn when a *user* instantiates Dict or when
295 # the "attribute notation for dict keys" is used.
Just van Rossum4c3d0542004-10-02 08:40:47 +0000296
Jack Jansen0ae32202003-04-09 13:25:43 +0000297 def __getattr__(self, attr):
Just van Rossum4c3d0542004-10-02 08:40:47 +0000298 try:
299 value = self[attr]
300 except KeyError:
301 raise AttributeError, attr
Just van Rossum368c0b22004-10-26 07:38:16 +0000302 from warnings import warn
303 warn("Attribute access from plist dicts is deprecated, use d[key] "
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000304 "notation instead", PendingDeprecationWarning, 2)
Just van Rossum4c3d0542004-10-02 08:40:47 +0000305 return value
306
307 def __setattr__(self, attr, value):
Just van Rossum368c0b22004-10-26 07:38:16 +0000308 from warnings import warn
309 warn("Attribute access from plist dicts is deprecated, use d[key] "
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000310 "notation instead", PendingDeprecationWarning, 2)
Just van Rossum4c3d0542004-10-02 08:40:47 +0000311 self[attr] = value
312
313 def __delattr__(self, attr):
314 try:
315 del self[attr]
316 except KeyError:
317 raise AttributeError, attr
Just van Rossum368c0b22004-10-26 07:38:16 +0000318 from warnings import warn
319 warn("Attribute access from plist dicts is deprecated, use d[key] "
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000320 "notation instead", PendingDeprecationWarning, 2)
Just van Rossum368c0b22004-10-26 07:38:16 +0000321
322class Dict(_InternalDict):
323
324 def __init__(self, **kwargs):
325 from warnings import warn
326 warn("The plistlib.Dict class is deprecated, use builtin dict instead",
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000327 PendingDeprecationWarning, 2)
Just van Rossum368c0b22004-10-26 07:38:16 +0000328 super(Dict, self).__init__(**kwargs)
Just van Rossum0ec27442002-11-19 22:01:02 +0000329
330
Just van Rossum368c0b22004-10-26 07:38:16 +0000331class Plist(_InternalDict):
Just van Rossum0ec27442002-11-19 22:01:02 +0000332
Just van Rossum1f74ef02004-10-26 10:30:55 +0000333 """This class has been deprecated. Use readPlist() and writePlist()
Just van Rossum368c0b22004-10-26 07:38:16 +0000334 functions instead, together with regular dict objects.
Jack Jansen0ae32202003-04-09 13:25:43 +0000335 """
Just van Rossum0ec27442002-11-19 22:01:02 +0000336
Just van Rossum86ca9022004-10-25 16:09:10 +0000337 def __init__(self, **kwargs):
338 from warnings import warn
339 warn("The Plist class is deprecated, use the readPlist() and "
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000340 "writePlist() functions instead", PendingDeprecationWarning, 2)
Just van Rossum86ca9022004-10-25 16:09:10 +0000341 super(Plist, self).__init__(**kwargs)
342
Jack Jansen0ae32202003-04-09 13:25:43 +0000343 def fromFile(cls, pathOrFile):
Just van Rossum86ca9022004-10-25 16:09:10 +0000344 """Deprecated. Use the readPlist() function instead."""
Just van Rossum4c3d0542004-10-02 08:40:47 +0000345 rootObject = readPlist(pathOrFile)
346 plist = cls()
347 plist.update(rootObject)
Jack Jansen0ae32202003-04-09 13:25:43 +0000348 return plist
349 fromFile = classmethod(fromFile)
Just van Rossum0ec27442002-11-19 22:01:02 +0000350
Jack Jansen0ae32202003-04-09 13:25:43 +0000351 def write(self, pathOrFile):
Just van Rossum86ca9022004-10-25 16:09:10 +0000352 """Deprecated. Use the writePlist() function instead."""
Just van Rossum4c3d0542004-10-02 08:40:47 +0000353 writePlist(self, pathOrFile)
Just van Rossum0ec27442002-11-19 22:01:02 +0000354
355
Just van Rossum1f74ef02004-10-26 10:30:55 +0000356def _encodeBase64(s, maxlinelength=76):
357 # copied from base64.encodestring(), with added maxlinelength argument
358 maxbinsize = (maxlinelength//4)*3
359 pieces = []
360 for i in range(0, len(s), maxbinsize):
361 chunk = s[i : i + maxbinsize]
362 pieces.append(binascii.b2a_base64(chunk))
363 return "".join(pieces)
364
Just van Rossum0ec27442002-11-19 22:01:02 +0000365class Data:
366
Jack Jansen0ae32202003-04-09 13:25:43 +0000367 """Wrapper for binary data."""
Just van Rossum0ec27442002-11-19 22:01:02 +0000368
Jack Jansen0ae32202003-04-09 13:25:43 +0000369 def __init__(self, data):
370 self.data = data
Just van Rossum0ec27442002-11-19 22:01:02 +0000371
Jack Jansen0ae32202003-04-09 13:25:43 +0000372 def fromBase64(cls, data):
Just van Rossum1f74ef02004-10-26 10:30:55 +0000373 # base64.decodestring just calls binascii.a2b_base64;
374 # it seems overkill to use both base64 and binascii.
375 return cls(binascii.a2b_base64(data))
Jack Jansen0ae32202003-04-09 13:25:43 +0000376 fromBase64 = classmethod(fromBase64)
Just van Rossum0ec27442002-11-19 22:01:02 +0000377
Just van Rossum1f74ef02004-10-26 10:30:55 +0000378 def asBase64(self, maxlinelength=76):
379 return _encodeBase64(self.data, maxlinelength)
Just van Rossum0ec27442002-11-19 22:01:02 +0000380
Jack Jansen0ae32202003-04-09 13:25:43 +0000381 def __cmp__(self, other):
382 if isinstance(other, self.__class__):
383 return cmp(self.data, other.data)
384 elif isinstance(other, str):
385 return cmp(self.data, other)
386 else:
387 return cmp(id(self), id(other))
Just van Rossum0ec27442002-11-19 22:01:02 +0000388
Jack Jansen0ae32202003-04-09 13:25:43 +0000389 def __repr__(self):
390 return "%s(%s)" % (self.__class__.__name__, repr(self.data))
Just van Rossum0ec27442002-11-19 22:01:02 +0000391
392
Just van Rossum0ec27442002-11-19 22:01:02 +0000393class PlistParser:
394
Jack Jansen0ae32202003-04-09 13:25:43 +0000395 def __init__(self):
396 self.stack = []
397 self.currentKey = None
398 self.root = None
Just van Rossum0ec27442002-11-19 22:01:02 +0000399
Just van Rossum95387a12004-10-25 15:10:42 +0000400 def parse(self, fileobj):
Jack Jansen0ae32202003-04-09 13:25:43 +0000401 from xml.parsers.expat import ParserCreate
402 parser = ParserCreate()
403 parser.StartElementHandler = self.handleBeginElement
404 parser.EndElementHandler = self.handleEndElement
405 parser.CharacterDataHandler = self.handleData
Just van Rossum95387a12004-10-25 15:10:42 +0000406 parser.ParseFile(fileobj)
Jack Jansen0ae32202003-04-09 13:25:43 +0000407 return self.root
Just van Rossum0ec27442002-11-19 22:01:02 +0000408
Jack Jansen0ae32202003-04-09 13:25:43 +0000409 def handleBeginElement(self, element, attrs):
410 self.data = []
411 handler = getattr(self, "begin_" + element, None)
412 if handler is not None:
413 handler(attrs)
Just van Rossum0ec27442002-11-19 22:01:02 +0000414
Jack Jansen0ae32202003-04-09 13:25:43 +0000415 def handleEndElement(self, element):
416 handler = getattr(self, "end_" + element, None)
417 if handler is not None:
418 handler()
Just van Rossum0ec27442002-11-19 22:01:02 +0000419
Jack Jansen0ae32202003-04-09 13:25:43 +0000420 def handleData(self, data):
421 self.data.append(data)
Just van Rossum0ec27442002-11-19 22:01:02 +0000422
Jack Jansen0ae32202003-04-09 13:25:43 +0000423 def addObject(self, value):
424 if self.currentKey is not None:
425 self.stack[-1][self.currentKey] = value
426 self.currentKey = None
427 elif not self.stack:
428 # this is the root object
Just van Rossum4c3d0542004-10-02 08:40:47 +0000429 self.root = value
Jack Jansen0ae32202003-04-09 13:25:43 +0000430 else:
431 self.stack[-1].append(value)
Just van Rossum0ec27442002-11-19 22:01:02 +0000432
Jack Jansen0ae32202003-04-09 13:25:43 +0000433 def getData(self):
434 data = "".join(self.data)
435 try:
436 data = data.encode("ascii")
437 except UnicodeError:
438 pass
439 self.data = []
440 return data
Just van Rossum0ec27442002-11-19 22:01:02 +0000441
Jack Jansen0ae32202003-04-09 13:25:43 +0000442 # element handlers
Just van Rossum0ec27442002-11-19 22:01:02 +0000443
Jack Jansen0ae32202003-04-09 13:25:43 +0000444 def begin_dict(self, attrs):
Just van Rossum368c0b22004-10-26 07:38:16 +0000445 d = _InternalDict()
Jack Jansen0ae32202003-04-09 13:25:43 +0000446 self.addObject(d)
447 self.stack.append(d)
448 def end_dict(self):
449 self.stack.pop()
Just van Rossum0ec27442002-11-19 22:01:02 +0000450
Jack Jansen0ae32202003-04-09 13:25:43 +0000451 def end_key(self):
452 self.currentKey = self.getData()
Just van Rossum0ec27442002-11-19 22:01:02 +0000453
Jack Jansen0ae32202003-04-09 13:25:43 +0000454 def begin_array(self, attrs):
455 a = []
456 self.addObject(a)
457 self.stack.append(a)
458 def end_array(self):
459 self.stack.pop()
Just van Rossum0ec27442002-11-19 22:01:02 +0000460
Jack Jansen0ae32202003-04-09 13:25:43 +0000461 def end_true(self):
462 self.addObject(True)
463 def end_false(self):
464 self.addObject(False)
465 def end_integer(self):
466 self.addObject(int(self.getData()))
467 def end_real(self):
468 self.addObject(float(self.getData()))
469 def end_string(self):
470 self.addObject(self.getData())
471 def end_data(self):
472 self.addObject(Data.fromBase64(self.getData()))
473 def end_date(self):
Just van Rossum7c944872004-10-26 07:20:26 +0000474 self.addObject(_dateFromString(self.getData()))