diff --git a/Lib/plat-mac/plistlib.py b/Lib/plat-mac/plistlib.py index 709e8e15fa1..f035729b76b 100644 --- a/Lib/plat-mac/plistlib.py +++ b/Lib/plat-mac/plistlib.py @@ -12,8 +12,8 @@ To parse a plist from a file, use the readPlist(pathOrFile) function, with a file name or a (readable) file object as the only argument. It returns the top level object (again, usually a dictionary). -To work with plist data in strings, you can use readPlistFromString() -and writePlistToString(). +To work with plist data in bytes objects, you can use readPlistFromBytes() +and writePlistToBytes(). Values can be strings, integers, floats, booleans, tuples, lists, dictionaries, Data or datetime.datetime objects. String values (including @@ -21,7 +21,7 @@ dictionary keys) may be unicode strings -- they will be written out as UTF-8. The plist type is supported through the Data class. This is a -thin wrapper around a Python string. +thin wrapper around a Python bytes object. Generate Plist example: @@ -36,8 +36,8 @@ Generate Plist example: aTrueValue=True, aFalseValue=False, ), - someData = Data(""), - someMoreData = Data("" * 10), + someData = Data(b""), + someMoreData = Data(b"" * 10), aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())), ) # unicode keys are possible, but a little awkward to use: @@ -52,7 +52,7 @@ Parse Plist example: __all__ = [ - "readPlist", "writePlist", "readPlistFromString", "writePlistToString", + "readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes", "readPlistFromResource", "writePlistToResource", "Plist", "Data", "Dict" ] @@ -60,7 +60,7 @@ __all__ = [ import binascii import datetime -from cStringIO import StringIO +from io import BytesIO import re @@ -71,7 +71,7 @@ def readPlist(pathOrFile): """ didOpen = 0 if isinstance(pathOrFile, str): - pathOrFile = open(pathOrFile) + pathOrFile = open(pathOrFile, 'rb') didOpen = 1 p = PlistParser() rootObject = p.parse(pathOrFile) @@ -86,7 +86,7 @@ def writePlist(rootObject, pathOrFile): """ didOpen = 0 if isinstance(pathOrFile, str): - pathOrFile = open(pathOrFile, "w") + pathOrFile = open(pathOrFile, 'wb') didOpen = 1 writer = PlistWriter(pathOrFile) writer.writeln("") @@ -96,16 +96,16 @@ def writePlist(rootObject, pathOrFile): pathOrFile.close() -def readPlistFromString(data): - """Read a plist data from a string. Return the root object. +def readPlistFromBytes(data): + """Read a plist data from a bytes object. Return the root object. """ - return readPlist(StringIO(data)) + return readPlist(BytesIO(data)) -def writePlistToString(rootObject): - """Return 'rootObject' as a plist-formatted string. +def writePlistToBytes(rootObject): + """Return 'rootObject' as a plist-formatted bytes object. """ - f = StringIO() + f = BytesIO() writePlist(rootObject, f) return f.getvalue() @@ -145,7 +145,6 @@ def writePlistToResource(rootObject, path, restype='plst', resid=0): class DumbXMLWriter: - def __init__(self, file, indentLevel=0, indent="\t"): self.file = file self.stack = [] @@ -172,9 +171,12 @@ class DumbXMLWriter: def writeln(self, line): if line: - self.file.write(self.indentLevel * self.indent + line + "\n") - else: - self.file.write("\n") + # plist has fixed encoding of utf-8 + if isinstance(line, str): + line = line.encode('utf-8') + self.file.write(self.indentLevel * self.indent) + self.file.write(line) + self.file.write('\n') # Contents should conform to a subset of ISO 8601 @@ -355,13 +357,15 @@ def _encodeBase64(s, maxlinelength=76): for i in range(0, len(s), maxbinsize): chunk = s[i : i + maxbinsize] pieces.append(binascii.b2a_base64(chunk)) - return "".join(pieces) + return b''.join(pieces) class Data: """Wrapper for binary data.""" def __init__(self, data): + if not isinstance(data, bytes): + raise TypeError("data must be as bytes") self.data = data def fromBase64(cls, data): @@ -426,11 +430,7 @@ class PlistParser: self.stack[-1].append(value) def getData(self): - data = "".join(self.data) - try: - data = data.encode("ascii") - except UnicodeError: - pass + data = ''.join(self.data) self.data = [] return data diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 1b8012e56de..4ef3ea69280 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -9,7 +9,7 @@ from test import test_support # This test data was generated through Cocoa's NSDictionary class -TESTDATA = """ +TESTDATA = b""" @@ -109,9 +109,9 @@ class TestPlistlib(unittest.TestCase): aFalseValue=False, deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]), ), - someData = plistlib.Data(""), - someMoreData = plistlib.Data("\0\1\2\3" * 10), - nestedData = [plistlib.Data("\0\1\2\3" * 10)], + someData = plistlib.Data(b""), + someMoreData = plistlib.Data(b"\0\1\2\3" * 10), + nestedData = [plistlib.Data(b"\0\1\2\3" * 10)], aDate = datetime.datetime(2004, 10, 26, 10, 33, 33), ) pl['\xc5benraa'] = "That was a unicode key." @@ -128,40 +128,32 @@ class TestPlistlib(unittest.TestCase): pl2 = plistlib.readPlist(test_support.TESTFN) self.assertEqual(dict(pl), dict(pl2)) - def test_string(self): + def test_bytes(self): pl = self._create() - data = plistlib.writePlistToString(pl) - pl2 = plistlib.readPlistFromString(data) + data = plistlib.writePlistToBytes(pl) + pl2 = plistlib.readPlistFromBytes(data) self.assertEqual(dict(pl), dict(pl2)) - data2 = plistlib.writePlistToString(pl2) + data2 = plistlib.writePlistToBytes(pl2) self.assertEqual(data, data2) def test_appleformatting(self): - pl = plistlib.readPlistFromString(TESTDATA) - data = plistlib.writePlistToString(pl) + pl = plistlib.readPlistFromBytes(TESTDATA) + data = plistlib.writePlistToBytes(pl) self.assertEqual(data, TESTDATA, "generated data was not identical to Apple's output") def test_appleformattingfromliteral(self): pl = self._create() - pl2 = plistlib.readPlistFromString(TESTDATA) + pl2 = plistlib.readPlistFromBytes(TESTDATA) self.assertEqual(dict(pl), dict(pl2), "generated data was not identical to Apple's output") - def test_stringio(self): - from StringIO import StringIO - f = StringIO() + def test_bytesio(self): + from io import BytesIO + b = BytesIO() pl = self._create() - plistlib.writePlist(pl, f) - pl2 = plistlib.readPlist(StringIO(f.getvalue())) - self.assertEqual(dict(pl), dict(pl2)) - - def test_cstringio(self): - from cStringIO import StringIO - f = StringIO() - pl = self._create() - plistlib.writePlist(pl, f) - pl2 = plistlib.readPlist(StringIO(f.getvalue())) + plistlib.writePlist(pl, b) + pl2 = plistlib.readPlist(BytesIO(b.getvalue())) self.assertEqual(dict(pl), dict(pl2)) def test_controlcharacters(self): @@ -170,17 +162,17 @@ class TestPlistlib(unittest.TestCase): testString = "string containing %s" % c if i >= 32 or c in "\r\n\t": # \r, \n and \t are the only legal control chars in XML - plistlib.writePlistToString(testString) + plistlib.writePlistToBytes(testString) else: self.assertRaises(ValueError, - plistlib.writePlistToString, + plistlib.writePlistToBytes, testString) def test_nondictroot(self): test1 = "abc" test2 = [1, 2, 3, "abc"] - result1 = plistlib.readPlistFromString(plistlib.writePlistToString(test1)) - result2 = plistlib.readPlistFromString(plistlib.writePlistToString(test2)) + result1 = plistlib.readPlistFromBytes(plistlib.writePlistToBytes(test1)) + result2 = plistlib.readPlistFromBytes(plistlib.writePlistToBytes(test2)) self.assertEqual(test1, result1) self.assertEqual(test2, result2)