1212with a file name or a (readable) file object as the only argument. It
1313returns the top level object (again, usually a dictionary).
1414
15- To work with plist data in strings , you can use readPlistFromString ()
16- and writePlistToString ().
15+ To work with plist data in bytes objects , you can use readPlistFromBytes ()
16+ and writePlistToBytes ().
1717
1818Values can be strings, integers, floats, booleans, tuples, lists,
1919dictionaries, Data or datetime.datetime objects. String values (including
2020dictionary keys) may be unicode strings -- they will be written out as
2121UTF-8.
2222
2323The <data> plist type is supported through the Data class. This is a
24- thin wrapper around a Python string .
24+ thin wrapper around a Python bytes object .
2525
2626Generate Plist example:
2727
3636 aTrueValue=True,
3737 aFalseValue=False,
3838 ),
39- someData = Data("<binary gunk>"),
40- someMoreData = Data("<lots of binary gunk>" * 10),
39+ someData = Data(b "<binary gunk>"),
40+ someMoreData = Data(b "<lots of binary gunk>" * 10),
4141 aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
4242 )
4343 # unicode keys are possible, but a little awkward to use:
5252
5353
5454__all__ = [
55- "readPlist" , "writePlist" , "readPlistFromString " , "writePlistToString " ,
55+ "readPlist" , "writePlist" , "readPlistFromBytes " , "writePlistToBytes " ,
5656 "readPlistFromResource" , "writePlistToResource" ,
5757 "Plist" , "Data" , "Dict"
5858]
5959# Note: the Plist and Dict classes have been deprecated.
6060
6161import binascii
6262import datetime
63- from cStringIO import StringIO
63+ from io import BytesIO
6464import re
6565
6666
@@ -71,7 +71,7 @@ def readPlist(pathOrFile):
7171 """
7272 didOpen = 0
7373 if isinstance (pathOrFile , str ):
74- pathOrFile = open (pathOrFile )
74+ pathOrFile = open (pathOrFile , 'rb' )
7575 didOpen = 1
7676 p = PlistParser ()
7777 rootObject = p .parse (pathOrFile )
@@ -86,7 +86,7 @@ def writePlist(rootObject, pathOrFile):
8686 """
8787 didOpen = 0
8888 if isinstance (pathOrFile , str ):
89- pathOrFile = open (pathOrFile , "w" )
89+ pathOrFile = open (pathOrFile , 'wb' )
9090 didOpen = 1
9191 writer = PlistWriter (pathOrFile )
9292 writer .writeln ("<plist version=\" 1.0\" >" )
@@ -96,16 +96,16 @@ def writePlist(rootObject, pathOrFile):
9696 pathOrFile .close ()
9797
9898
99- def readPlistFromString (data ):
100- """Read a plist data from a string . Return the root object.
99+ def readPlistFromBytes (data ):
100+ """Read a plist data from a bytes object . Return the root object.
101101 """
102- return readPlist (StringIO (data ))
102+ return readPlist (BytesIO (data ))
103103
104104
105- def writePlistToString (rootObject ):
106- """Return 'rootObject' as a plist-formatted string .
105+ def writePlistToBytes (rootObject ):
106+ """Return 'rootObject' as a plist-formatted bytes object .
107107 """
108- f = StringIO ()
108+ f = BytesIO ()
109109 writePlist (rootObject , f )
110110 return f .getvalue ()
111111
@@ -145,7 +145,6 @@ def writePlistToResource(rootObject, path, restype='plst', resid=0):
145145
146146
147147class DumbXMLWriter :
148-
149148 def __init__ (self , file , indentLevel = 0 , indent = "\t " ):
150149 self .file = file
151150 self .stack = []
@@ -172,9 +171,12 @@ def simpleElement(self, element, value=None):
172171
173172 def writeln (self , line ):
174173 if line :
175- self .file .write (self .indentLevel * self .indent + line + "\n " )
176- else :
177- self .file .write ("\n " )
174+ # 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 )
179+ self .file .write ('\n ' )
178180
179181
180182# Contents should conform to a subset of ISO 8601
@@ -355,13 +357,15 @@ def _encodeBase64(s, maxlinelength=76):
355357 for i in range (0 , len (s ), maxbinsize ):
356358 chunk = s [i : i + maxbinsize ]
357359 pieces .append (binascii .b2a_base64 (chunk ))
358- return "" .join (pieces )
360+ return b'' .join (pieces )
359361
360362class Data :
361363
362364 """Wrapper for binary data."""
363365
364366 def __init__ (self , data ):
367+ if not isinstance (data , bytes ):
368+ raise TypeError ("data must be as bytes" )
365369 self .data = data
366370
367371 def fromBase64 (cls , data ):
@@ -426,11 +430,7 @@ def addObject(self, value):
426430 self .stack [- 1 ].append (value )
427431
428432 def getData (self ):
429- data = "" .join (self .data )
430- try :
431- data = data .encode ("ascii" )
432- except UnicodeError :
433- pass
433+ data = '' .join (self .data )
434434 self .data = []
435435 return data
436436
0 commit comments