blob: 31d1e75fdf815be21e497493eea2826996b78b07 [file] [log] [blame]
Just van Rossum0ec27442002-11-19 22:01:02 +00001"""plistlib.py -- a tool to generate and parse MacOSX .plist files.
2
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 """
Benjamin Peterson23681932008-05-12 21:42:13 +0000117 warnings.warnpy3k("In 3.x, readPlistFromResource is removed.")
Just van Rossum95387a12004-10-25 15:10:42 +0000118 from Carbon.File import FSRef, FSGetResourceForkName
119 from Carbon.Files import fsRdPerm
120 from Carbon import Res
Just van Rossum95387a12004-10-25 15:10:42 +0000121 fsRef = FSRef(path)
122 resNum = Res.FSOpenResourceFile(fsRef, FSGetResourceForkName(), fsRdPerm)
123 Res.UseResFile(resNum)
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000124 plistData = Res.Get1Resource(restype, resid).data
Just van Rossum95387a12004-10-25 15:10:42 +0000125 Res.CloseResFile(resNum)
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000126 return readPlistFromString(plistData)
Just van Rossum95387a12004-10-25 15:10:42 +0000127
128
129def writePlistToResource(rootObject, path, restype='plst', resid=0):
130 """Write 'rootObject' as a plst resource to the resource fork of path.
131 """
Benjamin Peterson23681932008-05-12 21:42:13 +0000132 warnings.warnpy3k("In 3.x, writePlistToResource is removed.")
Just van Rossum95387a12004-10-25 15:10:42 +0000133 from Carbon.File import FSRef, FSGetResourceForkName
134 from Carbon.Files import fsRdWrPerm
135 from Carbon import Res
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000136 plistData = writePlistToString(rootObject)
Just van Rossum95387a12004-10-25 15:10:42 +0000137 fsRef = FSRef(path)
138 resNum = Res.FSOpenResourceFile(fsRef, FSGetResourceForkName(), fsRdWrPerm)
139 Res.UseResFile(resNum)
140 try:
141 Res.Get1Resource(restype, resid).RemoveResource()
142 except Res.Error:
143 pass
144 res = Res.Resource(plistData)
145 res.AddResource(restype, resid, '')
146 res.WriteResource()
147 Res.CloseResFile(resNum)
148
149
Just van Rossum0ec27442002-11-19 22:01:02 +0000150class DumbXMLWriter:
151
Just van Rossum4c3d0542004-10-02 08:40:47 +0000152 def __init__(self, file, indentLevel=0, indent="\t"):
Jack Jansen0ae32202003-04-09 13:25:43 +0000153 self.file = file
154 self.stack = []
Just van Rossum4c3d0542004-10-02 08:40:47 +0000155 self.indentLevel = indentLevel
156 self.indent = indent
Just van Rossum0ec27442002-11-19 22:01:02 +0000157
Jack Jansen0ae32202003-04-09 13:25:43 +0000158 def beginElement(self, element):
159 self.stack.append(element)
160 self.writeln("<%s>" % element)
161 self.indentLevel += 1
Just van Rossum0ec27442002-11-19 22:01:02 +0000162
Jack Jansen0ae32202003-04-09 13:25:43 +0000163 def endElement(self, element):
164 assert self.indentLevel > 0
165 assert self.stack.pop() == element
166 self.indentLevel -= 1
167 self.writeln("</%s>" % element)
Just van Rossum0ec27442002-11-19 22:01:02 +0000168
Jack Jansen0ae32202003-04-09 13:25:43 +0000169 def simpleElement(self, element, value=None):
Just van Rossum87316ec2003-07-10 14:26:06 +0000170 if value is not None:
Just van Rossum4c3d0542004-10-02 08:40:47 +0000171 value = _escapeAndEncode(value)
Jack Jansen0ae32202003-04-09 13:25:43 +0000172 self.writeln("<%s>%s</%s>" % (element, value, element))
173 else:
174 self.writeln("<%s/>" % element)
Just van Rossum0ec27442002-11-19 22:01:02 +0000175
Jack Jansen0ae32202003-04-09 13:25:43 +0000176 def writeln(self, line):
177 if line:
Just van Rossum4c3d0542004-10-02 08:40:47 +0000178 self.file.write(self.indentLevel * self.indent + line + "\n")
Jack Jansen0ae32202003-04-09 13:25:43 +0000179 else:
180 self.file.write("\n")
Just van Rossum0ec27442002-11-19 22:01:02 +0000181
182
Just van Rossum95387a12004-10-25 15:10:42 +0000183# Contents should conform to a subset of ISO 8601
184# (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units may be omitted with
185# a loss of precision)
186_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")
187
Just van Rossum7c944872004-10-26 07:20:26 +0000188def _dateFromString(s):
189 order = ('year', 'month', 'day', 'hour', 'minute', 'second')
190 gd = _dateParser.match(s).groupdict()
191 lst = []
192 for key in order:
193 val = gd[key]
194 if val is None:
195 break
196 lst.append(int(val))
197 return datetime.datetime(*lst)
198
199def _dateToString(d):
200 return '%04d-%02d-%02dT%02d:%02d:%02dZ' % (
201 d.year, d.month, d.day,
202 d.hour, d.minute, d.second
203 )
204
Just van Rossum26e811a2004-11-12 08:02:35 +0000205
Just van Rossum2dae7642004-11-12 09:36:12 +0000206# Regex to find any control chars, except for \t \n and \r
207_controlCharPat = re.compile(
Just van Rossumb84330d2004-11-12 08:14:49 +0000208 r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
209 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 +0000210
Just van Rossum4c3d0542004-10-02 08:40:47 +0000211def _escapeAndEncode(text):
Just van Rossum2dae7642004-11-12 09:36:12 +0000212 m = _controlCharPat.search(text)
213 if m is not None:
214 raise ValueError("strings can't contains control characters; "
215 "use plistlib.Data instead")
Just van Rossum4c3d0542004-10-02 08:40:47 +0000216 text = text.replace("\r\n", "\n") # convert DOS line endings
217 text = text.replace("\r", "\n") # convert Mac line endings
218 text = text.replace("&", "&amp;") # escape '&'
219 text = text.replace("<", "&lt;") # escape '<'
Just van Rossum8b8dece2004-10-26 10:11:00 +0000220 text = text.replace(">", "&gt;") # escape '>'
Just van Rossum4c3d0542004-10-02 08:40:47 +0000221 return text.encode("utf-8") # encode as UTF-8
Just van Rossum0ec27442002-11-19 22:01:02 +0000222
223
224PLISTHEADER = """\
225<?xml version="1.0" encoding="UTF-8"?>
226<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
227"""
228
229class PlistWriter(DumbXMLWriter):
230
Just van Rossum4c3d0542004-10-02 08:40:47 +0000231 def __init__(self, file, indentLevel=0, indent="\t", writeHeader=1):
232 if writeHeader:
233 file.write(PLISTHEADER)
234 DumbXMLWriter.__init__(self, file, indentLevel, indent)
Just van Rossum0ec27442002-11-19 22:01:02 +0000235
Jack Jansen0ae32202003-04-09 13:25:43 +0000236 def writeValue(self, value):
237 if isinstance(value, (str, unicode)):
238 self.simpleElement("string", value)
239 elif isinstance(value, bool):
240 # must switch for bool before int, as bool is a
241 # subclass of int...
242 if value:
243 self.simpleElement("true")
244 else:
245 self.simpleElement("false")
Christian Heimes06131882008-01-04 00:04:52 +0000246 elif isinstance(value, (int, long)):
247 self.simpleElement("integer", "%d" % value)
Jack Jansen0ae32202003-04-09 13:25:43 +0000248 elif isinstance(value, float):
Just van Rossum95387a12004-10-25 15:10:42 +0000249 self.simpleElement("real", repr(value))
Just van Rossum4c3d0542004-10-02 08:40:47 +0000250 elif isinstance(value, dict):
Jack Jansen0ae32202003-04-09 13:25:43 +0000251 self.writeDict(value)
252 elif isinstance(value, Data):
253 self.writeData(value)
Just van Rossum7c944872004-10-26 07:20:26 +0000254 elif isinstance(value, datetime.datetime):
255 self.simpleElement("date", _dateToString(value))
Jack Jansen0ae32202003-04-09 13:25:43 +0000256 elif isinstance(value, (tuple, list)):
257 self.writeArray(value)
258 else:
Just van Rossum94af32e2003-07-01 20:15:38 +0000259 raise TypeError("unsuported type: %s" % type(value))
Just van Rossum0ec27442002-11-19 22:01:02 +0000260
Jack Jansen0ae32202003-04-09 13:25:43 +0000261 def writeData(self, data):
262 self.beginElement("data")
Just van Rossum1f74ef02004-10-26 10:30:55 +0000263 self.indentLevel -= 1
264 maxlinelength = 76 - len(self.indent.replace("\t", " " * 8) *
265 self.indentLevel)
266 for line in data.asBase64(maxlinelength).split("\n"):
Jack Jansen0ae32202003-04-09 13:25:43 +0000267 if line:
268 self.writeln(line)
Just van Rossum1f74ef02004-10-26 10:30:55 +0000269 self.indentLevel += 1
Jack Jansen0ae32202003-04-09 13:25:43 +0000270 self.endElement("data")
Just van Rossum0ec27442002-11-19 22:01:02 +0000271
Jack Jansen0ae32202003-04-09 13:25:43 +0000272 def writeDict(self, d):
273 self.beginElement("dict")
274 items = d.items()
275 items.sort()
276 for key, value in items:
Just van Rossum94af32e2003-07-01 20:15:38 +0000277 if not isinstance(key, (str, unicode)):
278 raise TypeError("keys must be strings")
Jack Jansen0ae32202003-04-09 13:25:43 +0000279 self.simpleElement("key", key)
280 self.writeValue(value)
281 self.endElement("dict")
Just van Rossum0ec27442002-11-19 22:01:02 +0000282
Jack Jansen0ae32202003-04-09 13:25:43 +0000283 def writeArray(self, array):
284 self.beginElement("array")
285 for value in array:
286 self.writeValue(value)
287 self.endElement("array")
Just van Rossum0ec27442002-11-19 22:01:02 +0000288
289
Just van Rossum368c0b22004-10-26 07:38:16 +0000290class _InternalDict(dict):
Just van Rossum0ec27442002-11-19 22:01:02 +0000291
Just van Rossum368c0b22004-10-26 07:38:16 +0000292 # This class is needed while Dict is scheduled for deprecation:
293 # we only need to warn when a *user* instantiates Dict or when
294 # the "attribute notation for dict keys" is used.
Just van Rossum4c3d0542004-10-02 08:40:47 +0000295
Jack Jansen0ae32202003-04-09 13:25:43 +0000296 def __getattr__(self, attr):
Just van Rossum4c3d0542004-10-02 08:40:47 +0000297 try:
298 value = self[attr]
299 except KeyError:
300 raise AttributeError, attr
Just van Rossum368c0b22004-10-26 07:38:16 +0000301 from warnings import warn
302 warn("Attribute access from plist dicts is deprecated, use d[key] "
303 "notation instead", PendingDeprecationWarning)
Just van Rossum4c3d0542004-10-02 08:40:47 +0000304 return value
305
306 def __setattr__(self, attr, value):
Just van Rossum368c0b22004-10-26 07:38:16 +0000307 from warnings import warn
308 warn("Attribute access from plist dicts is deprecated, use d[key] "
309 "notation instead", PendingDeprecationWarning)
Just van Rossum4c3d0542004-10-02 08:40:47 +0000310 self[attr] = value
311
312 def __delattr__(self, attr):
313 try:
314 del self[attr]
315 except KeyError:
316 raise AttributeError, attr
Just van Rossum368c0b22004-10-26 07:38:16 +0000317 from warnings import warn
318 warn("Attribute access from plist dicts is deprecated, use d[key] "
319 "notation instead", PendingDeprecationWarning)
320
321class Dict(_InternalDict):
322
323 def __init__(self, **kwargs):
324 from warnings import warn
325 warn("The plistlib.Dict class is deprecated, use builtin dict instead",
326 PendingDeprecationWarning)
327 super(Dict, self).__init__(**kwargs)
Just van Rossum0ec27442002-11-19 22:01:02 +0000328
329
Just van Rossum368c0b22004-10-26 07:38:16 +0000330class Plist(_InternalDict):
Just van Rossum0ec27442002-11-19 22:01:02 +0000331
Just van Rossum1f74ef02004-10-26 10:30:55 +0000332 """This class has been deprecated. Use readPlist() and writePlist()
Just van Rossum368c0b22004-10-26 07:38:16 +0000333 functions instead, together with regular dict objects.
Jack Jansen0ae32202003-04-09 13:25:43 +0000334 """
Just van Rossum0ec27442002-11-19 22:01:02 +0000335
Just van Rossum86ca9022004-10-25 16:09:10 +0000336 def __init__(self, **kwargs):
337 from warnings import warn
338 warn("The Plist class is deprecated, use the readPlist() and "
339 "writePlist() functions instead", PendingDeprecationWarning)
340 super(Plist, self).__init__(**kwargs)
341
Jack Jansen0ae32202003-04-09 13:25:43 +0000342 def fromFile(cls, pathOrFile):
Just van Rossum86ca9022004-10-25 16:09:10 +0000343 """Deprecated. Use the readPlist() function instead."""
Just van Rossum4c3d0542004-10-02 08:40:47 +0000344 rootObject = readPlist(pathOrFile)
345 plist = cls()
346 plist.update(rootObject)
Jack Jansen0ae32202003-04-09 13:25:43 +0000347 return plist
348 fromFile = classmethod(fromFile)
Just van Rossum0ec27442002-11-19 22:01:02 +0000349
Jack Jansen0ae32202003-04-09 13:25:43 +0000350 def write(self, pathOrFile):
Just van Rossum86ca9022004-10-25 16:09:10 +0000351 """Deprecated. Use the writePlist() function instead."""
Just van Rossum4c3d0542004-10-02 08:40:47 +0000352 writePlist(self, pathOrFile)
Just van Rossum0ec27442002-11-19 22:01:02 +0000353
354
Just van Rossum1f74ef02004-10-26 10:30:55 +0000355def _encodeBase64(s, maxlinelength=76):
356 # copied from base64.encodestring(), with added maxlinelength argument
357 maxbinsize = (maxlinelength//4)*3
358 pieces = []
359 for i in range(0, len(s), maxbinsize):
360 chunk = s[i : i + maxbinsize]
361 pieces.append(binascii.b2a_base64(chunk))
362 return "".join(pieces)
363
Just van Rossum0ec27442002-11-19 22:01:02 +0000364class Data:
365
Jack Jansen0ae32202003-04-09 13:25:43 +0000366 """Wrapper for binary data."""
Just van Rossum0ec27442002-11-19 22:01:02 +0000367
Jack Jansen0ae32202003-04-09 13:25:43 +0000368 def __init__(self, data):
369 self.data = data
Just van Rossum0ec27442002-11-19 22:01:02 +0000370
Jack Jansen0ae32202003-04-09 13:25:43 +0000371 def fromBase64(cls, data):
Just van Rossum1f74ef02004-10-26 10:30:55 +0000372 # base64.decodestring just calls binascii.a2b_base64;
373 # it seems overkill to use both base64 and binascii.
374 return cls(binascii.a2b_base64(data))
Jack Jansen0ae32202003-04-09 13:25:43 +0000375 fromBase64 = classmethod(fromBase64)
Just van Rossum0ec27442002-11-19 22:01:02 +0000376
Just van Rossum1f74ef02004-10-26 10:30:55 +0000377 def asBase64(self, maxlinelength=76):
378 return _encodeBase64(self.data, maxlinelength)
Just van Rossum0ec27442002-11-19 22:01:02 +0000379
Jack Jansen0ae32202003-04-09 13:25:43 +0000380 def __cmp__(self, other):
381 if isinstance(other, self.__class__):
382 return cmp(self.data, other.data)
383 elif isinstance(other, str):
384 return cmp(self.data, other)
385 else:
386 return cmp(id(self), id(other))
Just van Rossum0ec27442002-11-19 22:01:02 +0000387
Jack Jansen0ae32202003-04-09 13:25:43 +0000388 def __repr__(self):
389 return "%s(%s)" % (self.__class__.__name__, repr(self.data))
Just van Rossum0ec27442002-11-19 22:01:02 +0000390
391
Just van Rossum0ec27442002-11-19 22:01:02 +0000392class PlistParser:
393
Jack Jansen0ae32202003-04-09 13:25:43 +0000394 def __init__(self):
395 self.stack = []
396 self.currentKey = None
397 self.root = None
Just van Rossum0ec27442002-11-19 22:01:02 +0000398
Just van Rossum95387a12004-10-25 15:10:42 +0000399 def parse(self, fileobj):
Jack Jansen0ae32202003-04-09 13:25:43 +0000400 from xml.parsers.expat import ParserCreate
401 parser = ParserCreate()
402 parser.StartElementHandler = self.handleBeginElement
403 parser.EndElementHandler = self.handleEndElement
404 parser.CharacterDataHandler = self.handleData
Just van Rossum95387a12004-10-25 15:10:42 +0000405 parser.ParseFile(fileobj)
Jack Jansen0ae32202003-04-09 13:25:43 +0000406 return self.root
Just van Rossum0ec27442002-11-19 22:01:02 +0000407
Jack Jansen0ae32202003-04-09 13:25:43 +0000408 def handleBeginElement(self, element, attrs):
409 self.data = []
410 handler = getattr(self, "begin_" + element, None)
411 if handler is not None:
412 handler(attrs)
Just van Rossum0ec27442002-11-19 22:01:02 +0000413
Jack Jansen0ae32202003-04-09 13:25:43 +0000414 def handleEndElement(self, element):
415 handler = getattr(self, "end_" + element, None)
416 if handler is not None:
417 handler()
Just van Rossum0ec27442002-11-19 22:01:02 +0000418
Jack Jansen0ae32202003-04-09 13:25:43 +0000419 def handleData(self, data):
420 self.data.append(data)
Just van Rossum0ec27442002-11-19 22:01:02 +0000421
Jack Jansen0ae32202003-04-09 13:25:43 +0000422 def addObject(self, value):
423 if self.currentKey is not None:
424 self.stack[-1][self.currentKey] = value
425 self.currentKey = None
426 elif not self.stack:
427 # this is the root object
Just van Rossum4c3d0542004-10-02 08:40:47 +0000428 self.root = value
Jack Jansen0ae32202003-04-09 13:25:43 +0000429 else:
430 self.stack[-1].append(value)
Just van Rossum0ec27442002-11-19 22:01:02 +0000431
Jack Jansen0ae32202003-04-09 13:25:43 +0000432 def getData(self):
433 data = "".join(self.data)
434 try:
435 data = data.encode("ascii")
436 except UnicodeError:
437 pass
438 self.data = []
439 return data
Just van Rossum0ec27442002-11-19 22:01:02 +0000440
Jack Jansen0ae32202003-04-09 13:25:43 +0000441 # element handlers
Just van Rossum0ec27442002-11-19 22:01:02 +0000442
Jack Jansen0ae32202003-04-09 13:25:43 +0000443 def begin_dict(self, attrs):
Just van Rossum368c0b22004-10-26 07:38:16 +0000444 d = _InternalDict()
Jack Jansen0ae32202003-04-09 13:25:43 +0000445 self.addObject(d)
446 self.stack.append(d)
447 def end_dict(self):
448 self.stack.pop()
Just van Rossum0ec27442002-11-19 22:01:02 +0000449
Jack Jansen0ae32202003-04-09 13:25:43 +0000450 def end_key(self):
451 self.currentKey = self.getData()
Just van Rossum0ec27442002-11-19 22:01:02 +0000452
Jack Jansen0ae32202003-04-09 13:25:43 +0000453 def begin_array(self, attrs):
454 a = []
455 self.addObject(a)
456 self.stack.append(a)
457 def end_array(self):
458 self.stack.pop()
Just van Rossum0ec27442002-11-19 22:01:02 +0000459
Jack Jansen0ae32202003-04-09 13:25:43 +0000460 def end_true(self):
461 self.addObject(True)
462 def end_false(self):
463 self.addObject(False)
464 def end_integer(self):
465 self.addObject(int(self.getData()))
466 def end_real(self):
467 self.addObject(float(self.getData()))
468 def end_string(self):
469 self.addObject(self.getData())
470 def end_data(self):
471 self.addObject(Data.fromBase64(self.getData()))
472 def end_date(self):
Just van Rossum7c944872004-10-26 07:20:26 +0000473 self.addObject(_dateFromString(self.getData()))