blob: 2a7957ee2583b86542e76f9e6d24c68944898110 [file] [log] [blame]
Just van Rossum0ec27442002-11-19 22:01:02 +00001"""plistlib.py -- a tool to generate and parse MacOSX .plist files.
2
Just van Rossum4c3d0542004-10-02 08:40:47 +00003The PropertList (.plist) file format is a simple XML pickle supporting
4basic 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
Guido van Rossumcd869d82007-08-07 14:26:40 +000015To work with plist data in bytes objects, you can use readPlistFromBytes()
16and writePlistToBytes().
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
Guido van Rossumcd869d82007-08-07 14:26:40 +000024thin wrapper around a Python bytes object.
Just van Rossum0ec27442002-11-19 22:01:02 +000025
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]],
31 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 ),
Guido van Rossumcd869d82007-08-07 14:26:40 +000039 someData = Data(b"<binary gunk>"),
40 someMoreData = Data(b"<lots of binary gunk>" * 10),
Just van Rossum7c944872004-10-26 07:20:26 +000041 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__ = [
Guido van Rossumcd869d82007-08-07 14:26:40 +000055 "readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes",
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
Guido van Rossumcd869d82007-08-07 14:26:40 +000063from io import BytesIO
Just van Rossum26e811a2004-11-12 08:02:35 +000064import re
Just van Rossum95387a12004-10-25 15:10:42 +000065
Just van Rossum0ec27442002-11-19 22:01:02 +000066
Just van Rossum4c3d0542004-10-02 08:40:47 +000067def readPlist(pathOrFile):
68 """Read a .plist file. 'pathOrFile' may either be a file name or a
69 (readable) file object. Return the unpacked root object (which
70 usually is a dictionary).
71 """
Collin Winterb9678e72007-08-30 18:11:17 +000072 didOpen = False
Walter Dörwaldaa97f042007-05-03 21:05:51 +000073 if isinstance(pathOrFile, str):
Guido van Rossumcd869d82007-08-07 14:26:40 +000074 pathOrFile = open(pathOrFile, 'rb')
Collin Winterb9678e72007-08-30 18:11:17 +000075 didOpen = True
Just van Rossum4c3d0542004-10-02 08:40:47 +000076 p = PlistParser()
77 rootObject = p.parse(pathOrFile)
78 if didOpen:
79 pathOrFile.close()
80 return rootObject
Just van Rossum0ec27442002-11-19 22:01:02 +000081
82
Just van Rossum4c3d0542004-10-02 08:40:47 +000083def writePlist(rootObject, pathOrFile):
84 """Write 'rootObject' to a .plist file. 'pathOrFile' may either be a
85 file name or a (writable) file object.
86 """
Collin Winterb9678e72007-08-30 18:11:17 +000087 didOpen = False
Walter Dörwaldaa97f042007-05-03 21:05:51 +000088 if isinstance(pathOrFile, str):
Guido van Rossumcd869d82007-08-07 14:26:40 +000089 pathOrFile = open(pathOrFile, 'wb')
Collin Winterb9678e72007-08-30 18:11:17 +000090 didOpen = True
Just van Rossum4c3d0542004-10-02 08:40:47 +000091 writer = PlistWriter(pathOrFile)
92 writer.writeln("<plist version=\"1.0\">")
93 writer.writeValue(rootObject)
94 writer.writeln("</plist>")
95 if didOpen:
96 pathOrFile.close()
Just van Rossum0ec27442002-11-19 22:01:02 +000097
98
Guido van Rossumcd869d82007-08-07 14:26:40 +000099def readPlistFromBytes(data):
100 """Read a plist data from a bytes object. Return the root object.
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000101 """
Guido van Rossumcd869d82007-08-07 14:26:40 +0000102 return readPlist(BytesIO(data))
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000103
104
Guido van Rossumcd869d82007-08-07 14:26:40 +0000105def writePlistToBytes(rootObject):
106 """Return 'rootObject' as a plist-formatted bytes object.
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000107 """
Guido van Rossumcd869d82007-08-07 14:26:40 +0000108 f = BytesIO()
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000109 writePlist(rootObject, f)
110 return f.getvalue()
111
112
Just van Rossum95387a12004-10-25 15:10:42 +0000113def readPlistFromResource(path, restype='plst', resid=0):
114 """Read plst resource from the resource fork of path.
115 """
116 from Carbon.File import FSRef, FSGetResourceForkName
117 from Carbon.Files import fsRdPerm
118 from Carbon import Res
Just van Rossum95387a12004-10-25 15:10:42 +0000119 fsRef = FSRef(path)
120 resNum = Res.FSOpenResourceFile(fsRef, FSGetResourceForkName(), fsRdPerm)
121 Res.UseResFile(resNum)
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000122 plistData = Res.Get1Resource(restype, resid).data
Just van Rossum95387a12004-10-25 15:10:42 +0000123 Res.CloseResFile(resNum)
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000124 return readPlistFromString(plistData)
Just van Rossum95387a12004-10-25 15:10:42 +0000125
126
127def writePlistToResource(rootObject, path, restype='plst', resid=0):
128 """Write 'rootObject' as a plst resource to the resource fork of path.
129 """
130 from Carbon.File import FSRef, FSGetResourceForkName
131 from Carbon.Files import fsRdWrPerm
132 from Carbon import Res
Just van Rossumc6fdd1b2004-10-26 06:50:50 +0000133 plistData = writePlistToString(rootObject)
Just van Rossum95387a12004-10-25 15:10:42 +0000134 fsRef = FSRef(path)
135 resNum = Res.FSOpenResourceFile(fsRef, FSGetResourceForkName(), fsRdWrPerm)
136 Res.UseResFile(resNum)
137 try:
138 Res.Get1Resource(restype, resid).RemoveResource()
139 except Res.Error:
140 pass
141 res = Res.Resource(plistData)
142 res.AddResource(restype, resid, '')
143 res.WriteResource()
144 Res.CloseResFile(resNum)
145
146
Just van Rossum0ec27442002-11-19 22:01:02 +0000147class DumbXMLWriter:
Just van Rossum4c3d0542004-10-02 08:40:47 +0000148 def __init__(self, file, indentLevel=0, indent="\t"):
Jack Jansen0ae32202003-04-09 13:25:43 +0000149 self.file = file
150 self.stack = []
Just van Rossum4c3d0542004-10-02 08:40:47 +0000151 self.indentLevel = indentLevel
152 self.indent = indent
Just van Rossum0ec27442002-11-19 22:01:02 +0000153
Jack Jansen0ae32202003-04-09 13:25:43 +0000154 def beginElement(self, element):
155 self.stack.append(element)
156 self.writeln("<%s>" % element)
157 self.indentLevel += 1
Just van Rossum0ec27442002-11-19 22:01:02 +0000158
Jack Jansen0ae32202003-04-09 13:25:43 +0000159 def endElement(self, element):
160 assert self.indentLevel > 0
161 assert self.stack.pop() == element
162 self.indentLevel -= 1
163 self.writeln("</%s>" % element)
Just van Rossum0ec27442002-11-19 22:01:02 +0000164
Jack Jansen0ae32202003-04-09 13:25:43 +0000165 def simpleElement(self, element, value=None):
Just van Rossum87316ec2003-07-10 14:26:06 +0000166 if value is not None:
Just van Rossum4c3d0542004-10-02 08:40:47 +0000167 value = _escapeAndEncode(value)
Jack Jansen0ae32202003-04-09 13:25:43 +0000168 self.writeln("<%s>%s</%s>" % (element, value, element))
169 else:
170 self.writeln("<%s/>" % element)
Just van Rossum0ec27442002-11-19 22:01:02 +0000171
Jack Jansen0ae32202003-04-09 13:25:43 +0000172 def writeln(self, line):
173 if line:
Guido van Rossumcd869d82007-08-07 14:26:40 +0000174 # plist has fixed encoding of utf-8
175 if isinstance(line, str):
176 line = line.encode('utf-8')
177 self.file.write(self.indentLevel * self.indent)
178 self.file.write(line)
Guido van Rossum6dab7952007-08-27 17:25:39 +0000179 self.file.write(b'\n')
Just van Rossum0ec27442002-11-19 22:01:02 +0000180
181
Just van Rossum95387a12004-10-25 15:10:42 +0000182# Contents should conform to a subset of ISO 8601
183# (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units may be omitted with
184# a loss of precision)
185_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")
186
Just van Rossum7c944872004-10-26 07:20:26 +0000187def _dateFromString(s):
188 order = ('year', 'month', 'day', 'hour', 'minute', 'second')
189 gd = _dateParser.match(s).groupdict()
190 lst = []
191 for key in order:
192 val = gd[key]
193 if val is None:
194 break
195 lst.append(int(val))
196 return datetime.datetime(*lst)
197
198def _dateToString(d):
199 return '%04d-%02d-%02dT%02d:%02d:%02dZ' % (
200 d.year, d.month, d.day,
201 d.hour, d.minute, d.second
202 )
203
Just van Rossum26e811a2004-11-12 08:02:35 +0000204
Just van Rossum2dae7642004-11-12 09:36:12 +0000205# Regex to find any control chars, except for \t \n and \r
206_controlCharPat = re.compile(
Just van Rossumb84330d2004-11-12 08:14:49 +0000207 r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
208 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 +0000209
Just van Rossum4c3d0542004-10-02 08:40:47 +0000210def _escapeAndEncode(text):
Just van Rossum2dae7642004-11-12 09:36:12 +0000211 m = _controlCharPat.search(text)
212 if m is not None:
213 raise ValueError("strings can't contains control characters; "
214 "use plistlib.Data instead")
Just van Rossum4c3d0542004-10-02 08:40:47 +0000215 text = text.replace("\r\n", "\n") # convert DOS line endings
216 text = text.replace("\r", "\n") # convert Mac line endings
217 text = text.replace("&", "&amp;") # escape '&'
218 text = text.replace("<", "&lt;") # escape '<'
Just van Rossum8b8dece2004-10-26 10:11:00 +0000219 text = text.replace(">", "&gt;") # escape '>'
Just van Rossum4c3d0542004-10-02 08:40:47 +0000220 return text.encode("utf-8") # encode as UTF-8
Just van Rossum0ec27442002-11-19 22:01:02 +0000221
222
Guido van Rossum6dab7952007-08-27 17:25:39 +0000223PLISTHEADER = b"""\
Just van Rossum0ec27442002-11-19 22:01:02 +0000224<?xml version="1.0" encoding="UTF-8"?>
225<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
226"""
227
228class PlistWriter(DumbXMLWriter):
229
Guido van Rossum6dab7952007-08-27 17:25:39 +0000230 def __init__(self, file, indentLevel=0, indent=b"\t", writeHeader=1):
Just van Rossum4c3d0542004-10-02 08:40:47 +0000231 if writeHeader:
232 file.write(PLISTHEADER)
233 DumbXMLWriter.__init__(self, file, indentLevel, indent)
Just van Rossum0ec27442002-11-19 22:01:02 +0000234
Jack Jansen0ae32202003-04-09 13:25:43 +0000235 def writeValue(self, value):
Walter Dörwaldaa97f042007-05-03 21:05:51 +0000236 if isinstance(value, str):
Jack Jansen0ae32202003-04-09 13:25:43 +0000237 self.simpleElement("string", value)
238 elif isinstance(value, bool):
239 # must switch for bool before int, as bool is a
240 # subclass of int...
241 if value:
242 self.simpleElement("true")
243 else:
244 self.simpleElement("false")
245 elif isinstance(value, int):
246 self.simpleElement("integer", str(value))
247 elif isinstance(value, float):
Just van Rossum95387a12004-10-25 15:10:42 +0000248 self.simpleElement("real", repr(value))
Just van Rossum4c3d0542004-10-02 08:40:47 +0000249 elif isinstance(value, dict):
Jack Jansen0ae32202003-04-09 13:25:43 +0000250 self.writeDict(value)
251 elif isinstance(value, Data):
252 self.writeData(value)
Just van Rossum7c944872004-10-26 07:20:26 +0000253 elif isinstance(value, datetime.datetime):
254 self.simpleElement("date", _dateToString(value))
Jack Jansen0ae32202003-04-09 13:25:43 +0000255 elif isinstance(value, (tuple, list)):
256 self.writeArray(value)
257 else:
Just van Rossum94af32e2003-07-01 20:15:38 +0000258 raise TypeError("unsuported type: %s" % type(value))
Just van Rossum0ec27442002-11-19 22:01:02 +0000259
Jack Jansen0ae32202003-04-09 13:25:43 +0000260 def writeData(self, data):
261 self.beginElement("data")
Just van Rossum1f74ef02004-10-26 10:30:55 +0000262 self.indentLevel -= 1
263 maxlinelength = 76 - len(self.indent.replace("\t", " " * 8) *
264 self.indentLevel)
265 for line in data.asBase64(maxlinelength).split("\n"):
Jack Jansen0ae32202003-04-09 13:25:43 +0000266 if line:
267 self.writeln(line)
Just van Rossum1f74ef02004-10-26 10:30:55 +0000268 self.indentLevel += 1
Jack Jansen0ae32202003-04-09 13:25:43 +0000269 self.endElement("data")
Just van Rossum0ec27442002-11-19 22:01:02 +0000270
Jack Jansen0ae32202003-04-09 13:25:43 +0000271 def writeDict(self, d):
272 self.beginElement("dict")
Brett Cannonb38e2bc2007-02-21 21:18:18 +0000273 items = sorted(d.items())
Jack Jansen0ae32202003-04-09 13:25:43 +0000274 for key, value in items:
Walter Dörwaldaa97f042007-05-03 21:05:51 +0000275 if not isinstance(key, str):
Just van Rossum94af32e2003-07-01 20:15:38 +0000276 raise TypeError("keys must be strings")
Jack Jansen0ae32202003-04-09 13:25:43 +0000277 self.simpleElement("key", key)
278 self.writeValue(value)
279 self.endElement("dict")
Just van Rossum0ec27442002-11-19 22:01:02 +0000280
Jack Jansen0ae32202003-04-09 13:25:43 +0000281 def writeArray(self, array):
282 self.beginElement("array")
283 for value in array:
284 self.writeValue(value)
285 self.endElement("array")
Just van Rossum0ec27442002-11-19 22:01:02 +0000286
287
Just van Rossum368c0b22004-10-26 07:38:16 +0000288class _InternalDict(dict):
Just van Rossum0ec27442002-11-19 22:01:02 +0000289
Just van Rossum368c0b22004-10-26 07:38:16 +0000290 # This class is needed while Dict is scheduled for deprecation:
291 # we only need to warn when a *user* instantiates Dict or when
292 # the "attribute notation for dict keys" is used.
Just van Rossum4c3d0542004-10-02 08:40:47 +0000293
Jack Jansen0ae32202003-04-09 13:25:43 +0000294 def __getattr__(self, attr):
Just van Rossum4c3d0542004-10-02 08:40:47 +0000295 try:
296 value = self[attr]
297 except KeyError:
Collin Wintere45be282007-08-23 00:01:55 +0000298 raise AttributeError(attr)
Just van Rossum368c0b22004-10-26 07:38:16 +0000299 from warnings import warn
300 warn("Attribute access from plist dicts is deprecated, use d[key] "
301 "notation instead", PendingDeprecationWarning)
Just van Rossum4c3d0542004-10-02 08:40:47 +0000302 return value
303
304 def __setattr__(self, attr, value):
Just van Rossum368c0b22004-10-26 07:38:16 +0000305 from warnings import warn
306 warn("Attribute access from plist dicts is deprecated, use d[key] "
307 "notation instead", PendingDeprecationWarning)
Just van Rossum4c3d0542004-10-02 08:40:47 +0000308 self[attr] = value
309
310 def __delattr__(self, attr):
311 try:
312 del self[attr]
313 except KeyError:
Collin Wintere45be282007-08-23 00:01:55 +0000314 raise AttributeError(attr)
Just van Rossum368c0b22004-10-26 07:38:16 +0000315 from warnings import warn
316 warn("Attribute access from plist dicts is deprecated, use d[key] "
317 "notation instead", PendingDeprecationWarning)
318
319class Dict(_InternalDict):
320
321 def __init__(self, **kwargs):
322 from warnings import warn
323 warn("The plistlib.Dict class is deprecated, use builtin dict instead",
324 PendingDeprecationWarning)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000325 super().__init__(**kwargs)
Just van Rossum0ec27442002-11-19 22:01:02 +0000326
327
Just van Rossum368c0b22004-10-26 07:38:16 +0000328class Plist(_InternalDict):
Just van Rossum0ec27442002-11-19 22:01:02 +0000329
Just van Rossum1f74ef02004-10-26 10:30:55 +0000330 """This class has been deprecated. Use readPlist() and writePlist()
Just van Rossum368c0b22004-10-26 07:38:16 +0000331 functions instead, together with regular dict objects.
Jack Jansen0ae32202003-04-09 13:25:43 +0000332 """
Just van Rossum0ec27442002-11-19 22:01:02 +0000333
Just van Rossum86ca9022004-10-25 16:09:10 +0000334 def __init__(self, **kwargs):
335 from warnings import warn
336 warn("The Plist class is deprecated, use the readPlist() and "
337 "writePlist() functions instead", PendingDeprecationWarning)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000338 super().__init__(**kwargs)
Just van Rossum86ca9022004-10-25 16:09:10 +0000339
Jack Jansen0ae32202003-04-09 13:25:43 +0000340 def fromFile(cls, pathOrFile):
Just van Rossum86ca9022004-10-25 16:09:10 +0000341 """Deprecated. Use the readPlist() function instead."""
Just van Rossum4c3d0542004-10-02 08:40:47 +0000342 rootObject = readPlist(pathOrFile)
343 plist = cls()
344 plist.update(rootObject)
Jack Jansen0ae32202003-04-09 13:25:43 +0000345 return plist
346 fromFile = classmethod(fromFile)
Just van Rossum0ec27442002-11-19 22:01:02 +0000347
Jack Jansen0ae32202003-04-09 13:25:43 +0000348 def write(self, pathOrFile):
Just van Rossum86ca9022004-10-25 16:09:10 +0000349 """Deprecated. Use the writePlist() function instead."""
Just van Rossum4c3d0542004-10-02 08:40:47 +0000350 writePlist(self, pathOrFile)
Just van Rossum0ec27442002-11-19 22:01:02 +0000351
352
Just van Rossum1f74ef02004-10-26 10:30:55 +0000353def _encodeBase64(s, maxlinelength=76):
354 # copied from base64.encodestring(), with added maxlinelength argument
355 maxbinsize = (maxlinelength//4)*3
356 pieces = []
357 for i in range(0, len(s), maxbinsize):
358 chunk = s[i : i + maxbinsize]
359 pieces.append(binascii.b2a_base64(chunk))
Guido van Rossumcd869d82007-08-07 14:26:40 +0000360 return b''.join(pieces)
Just van Rossum1f74ef02004-10-26 10:30:55 +0000361
Just van Rossum0ec27442002-11-19 22:01:02 +0000362class Data:
363
Jack Jansen0ae32202003-04-09 13:25:43 +0000364 """Wrapper for binary data."""
Just van Rossum0ec27442002-11-19 22:01:02 +0000365
Jack Jansen0ae32202003-04-09 13:25:43 +0000366 def __init__(self, data):
Guido van Rossumcd869d82007-08-07 14:26:40 +0000367 if not isinstance(data, bytes):
368 raise TypeError("data must be as bytes")
Jack Jansen0ae32202003-04-09 13:25:43 +0000369 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
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000380 def __eq__(self, other):
Jack Jansen0ae32202003-04-09 13:25:43 +0000381 if isinstance(other, self.__class__):
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000382 return self.data == other.data
Jack Jansen0ae32202003-04-09 13:25:43 +0000383 elif isinstance(other, str):
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000384 return self.data == other
Jack Jansen0ae32202003-04-09 13:25:43 +0000385 else:
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000386 return 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):
Guido van Rossumcd869d82007-08-07 14:26:40 +0000433 data = ''.join(self.data)
Jack Jansen0ae32202003-04-09 13:25:43 +0000434 self.data = []
435 return data
Just van Rossum0ec27442002-11-19 22:01:02 +0000436
Jack Jansen0ae32202003-04-09 13:25:43 +0000437 # element handlers
Just van Rossum0ec27442002-11-19 22:01:02 +0000438
Jack Jansen0ae32202003-04-09 13:25:43 +0000439 def begin_dict(self, attrs):
Just van Rossum368c0b22004-10-26 07:38:16 +0000440 d = _InternalDict()
Jack Jansen0ae32202003-04-09 13:25:43 +0000441 self.addObject(d)
442 self.stack.append(d)
443 def end_dict(self):
444 self.stack.pop()
Just van Rossum0ec27442002-11-19 22:01:02 +0000445
Jack Jansen0ae32202003-04-09 13:25:43 +0000446 def end_key(self):
447 self.currentKey = self.getData()
Just van Rossum0ec27442002-11-19 22:01:02 +0000448
Jack Jansen0ae32202003-04-09 13:25:43 +0000449 def begin_array(self, attrs):
450 a = []
451 self.addObject(a)
452 self.stack.append(a)
453 def end_array(self):
454 self.stack.pop()
Just van Rossum0ec27442002-11-19 22:01:02 +0000455
Jack Jansen0ae32202003-04-09 13:25:43 +0000456 def end_true(self):
457 self.addObject(True)
458 def end_false(self):
459 self.addObject(False)
460 def end_integer(self):
461 self.addObject(int(self.getData()))
462 def end_real(self):
463 self.addObject(float(self.getData()))
464 def end_string(self):
465 self.addObject(self.getData())
466 def end_data(self):
467 self.addObject(Data.fromBase64(self.getData()))
468 def end_date(self):
Just van Rossum7c944872004-10-26 07:20:26 +0000469 self.addObject(_dateFromString(self.getData()))