Commit cd869d8d authored by Guido van Rossum's avatar Guido van Rossum

SF patch# 1769016 by James Brotchie.

Change plistlib to use bytes instead of strings.
Fix test_plistlib accordingly.
parent ca8dd918
......@@ -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 <data> 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("<binary gunk>"),
someMoreData = Data("<lots of binary gunk>" * 10),
someData = Data(b"<binary gunk>"),
someMoreData = Data(b"<lots of binary gunk>" * 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("<plist version=\"1.0\">")
......@@ -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
......
......@@ -9,7 +9,7 @@ from test import test_support
# This test data was generated through Cocoa's NSDictionary class
TESTDATA = """<?xml version="1.0" encoding="UTF-8"?>
TESTDATA = b"""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
......@@ -109,9 +109,9 @@ class TestPlistlib(unittest.TestCase):
aFalseValue=False,
deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]),
),
someData = plistlib.Data("<binary gunk>"),
someMoreData = plistlib.Data("<lots of binary gunk>\0\1\2\3" * 10),
nestedData = [plistlib.Data("<lots of binary gunk>\0\1\2\3" * 10)],
someData = plistlib.Data(b"<binary gunk>"),
someMoreData = plistlib.Data(b"<lots of binary gunk>\0\1\2\3" * 10),
nestedData = [plistlib.Data(b"<lots of binary gunk>\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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment