mirror of https://github.com/python/cpython
SF patch# 1769016 by James Brotchie.
Change plistlib to use bytes instead of strings. Fix test_plistlib accordingly.
This commit is contained in:
parent
ca8dd9182e
commit
cd869d8d41
|
@ -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
|
with a file name or a (readable) file object as the only argument. It
|
||||||
returns the top level object (again, usually a dictionary).
|
returns the top level object (again, usually a dictionary).
|
||||||
|
|
||||||
To work with plist data in strings, you can use readPlistFromString()
|
To work with plist data in bytes objects, you can use readPlistFromBytes()
|
||||||
and writePlistToString().
|
and writePlistToBytes().
|
||||||
|
|
||||||
Values can be strings, integers, floats, booleans, tuples, lists,
|
Values can be strings, integers, floats, booleans, tuples, lists,
|
||||||
dictionaries, Data or datetime.datetime objects. String values (including
|
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.
|
UTF-8.
|
||||||
|
|
||||||
The <data> plist type is supported through the Data class. This is a
|
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:
|
Generate Plist example:
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ Generate Plist example:
|
||||||
aTrueValue=True,
|
aTrueValue=True,
|
||||||
aFalseValue=False,
|
aFalseValue=False,
|
||||||
),
|
),
|
||||||
someData = Data("<binary gunk>"),
|
someData = Data(b"<binary gunk>"),
|
||||||
someMoreData = Data("<lots of binary gunk>" * 10),
|
someMoreData = Data(b"<lots of binary gunk>" * 10),
|
||||||
aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
|
aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
|
||||||
)
|
)
|
||||||
# unicode keys are possible, but a little awkward to use:
|
# unicode keys are possible, but a little awkward to use:
|
||||||
|
@ -52,7 +52,7 @@ Parse Plist example:
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"readPlist", "writePlist", "readPlistFromString", "writePlistToString",
|
"readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes",
|
||||||
"readPlistFromResource", "writePlistToResource",
|
"readPlistFromResource", "writePlistToResource",
|
||||||
"Plist", "Data", "Dict"
|
"Plist", "Data", "Dict"
|
||||||
]
|
]
|
||||||
|
@ -60,7 +60,7 @@ __all__ = [
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
import datetime
|
import datetime
|
||||||
from cStringIO import StringIO
|
from io import BytesIO
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ def readPlist(pathOrFile):
|
||||||
"""
|
"""
|
||||||
didOpen = 0
|
didOpen = 0
|
||||||
if isinstance(pathOrFile, str):
|
if isinstance(pathOrFile, str):
|
||||||
pathOrFile = open(pathOrFile)
|
pathOrFile = open(pathOrFile, 'rb')
|
||||||
didOpen = 1
|
didOpen = 1
|
||||||
p = PlistParser()
|
p = PlistParser()
|
||||||
rootObject = p.parse(pathOrFile)
|
rootObject = p.parse(pathOrFile)
|
||||||
|
@ -86,7 +86,7 @@ def writePlist(rootObject, pathOrFile):
|
||||||
"""
|
"""
|
||||||
didOpen = 0
|
didOpen = 0
|
||||||
if isinstance(pathOrFile, str):
|
if isinstance(pathOrFile, str):
|
||||||
pathOrFile = open(pathOrFile, "w")
|
pathOrFile = open(pathOrFile, 'wb')
|
||||||
didOpen = 1
|
didOpen = 1
|
||||||
writer = PlistWriter(pathOrFile)
|
writer = PlistWriter(pathOrFile)
|
||||||
writer.writeln("<plist version=\"1.0\">")
|
writer.writeln("<plist version=\"1.0\">")
|
||||||
|
@ -96,16 +96,16 @@ def writePlist(rootObject, pathOrFile):
|
||||||
pathOrFile.close()
|
pathOrFile.close()
|
||||||
|
|
||||||
|
|
||||||
def readPlistFromString(data):
|
def readPlistFromBytes(data):
|
||||||
"""Read a plist data from a string. Return the root object.
|
"""Read a plist data from a bytes object. Return the root object.
|
||||||
"""
|
"""
|
||||||
return readPlist(StringIO(data))
|
return readPlist(BytesIO(data))
|
||||||
|
|
||||||
|
|
||||||
def writePlistToString(rootObject):
|
def writePlistToBytes(rootObject):
|
||||||
"""Return 'rootObject' as a plist-formatted string.
|
"""Return 'rootObject' as a plist-formatted bytes object.
|
||||||
"""
|
"""
|
||||||
f = StringIO()
|
f = BytesIO()
|
||||||
writePlist(rootObject, f)
|
writePlist(rootObject, f)
|
||||||
return f.getvalue()
|
return f.getvalue()
|
||||||
|
|
||||||
|
@ -145,7 +145,6 @@ def writePlistToResource(rootObject, path, restype='plst', resid=0):
|
||||||
|
|
||||||
|
|
||||||
class DumbXMLWriter:
|
class DumbXMLWriter:
|
||||||
|
|
||||||
def __init__(self, file, indentLevel=0, indent="\t"):
|
def __init__(self, file, indentLevel=0, indent="\t"):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.stack = []
|
self.stack = []
|
||||||
|
@ -172,9 +171,12 @@ class DumbXMLWriter:
|
||||||
|
|
||||||
def writeln(self, line):
|
def writeln(self, line):
|
||||||
if line:
|
if line:
|
||||||
self.file.write(self.indentLevel * self.indent + line + "\n")
|
# plist has fixed encoding of utf-8
|
||||||
else:
|
if isinstance(line, str):
|
||||||
self.file.write("\n")
|
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
|
# 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):
|
for i in range(0, len(s), maxbinsize):
|
||||||
chunk = s[i : i + maxbinsize]
|
chunk = s[i : i + maxbinsize]
|
||||||
pieces.append(binascii.b2a_base64(chunk))
|
pieces.append(binascii.b2a_base64(chunk))
|
||||||
return "".join(pieces)
|
return b''.join(pieces)
|
||||||
|
|
||||||
class Data:
|
class Data:
|
||||||
|
|
||||||
"""Wrapper for binary data."""
|
"""Wrapper for binary data."""
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
|
if not isinstance(data, bytes):
|
||||||
|
raise TypeError("data must be as bytes")
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
def fromBase64(cls, data):
|
def fromBase64(cls, data):
|
||||||
|
@ -426,11 +430,7 @@ class PlistParser:
|
||||||
self.stack[-1].append(value)
|
self.stack[-1].append(value)
|
||||||
|
|
||||||
def getData(self):
|
def getData(self):
|
||||||
data = "".join(self.data)
|
data = ''.join(self.data)
|
||||||
try:
|
|
||||||
data = data.encode("ascii")
|
|
||||||
except UnicodeError:
|
|
||||||
pass
|
|
||||||
self.data = []
|
self.data = []
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ from test import test_support
|
||||||
|
|
||||||
|
|
||||||
# This test data was generated through Cocoa's NSDictionary class
|
# 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" \
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \
|
||||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
|
@ -109,9 +109,9 @@ class TestPlistlib(unittest.TestCase):
|
||||||
aFalseValue=False,
|
aFalseValue=False,
|
||||||
deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]),
|
deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]),
|
||||||
),
|
),
|
||||||
someData = plistlib.Data("<binary gunk>"),
|
someData = plistlib.Data(b"<binary gunk>"),
|
||||||
someMoreData = plistlib.Data("<lots of binary gunk>\0\1\2\3" * 10),
|
someMoreData = plistlib.Data(b"<lots of binary gunk>\0\1\2\3" * 10),
|
||||||
nestedData = [plistlib.Data("<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),
|
aDate = datetime.datetime(2004, 10, 26, 10, 33, 33),
|
||||||
)
|
)
|
||||||
pl['\xc5benraa'] = "That was a unicode key."
|
pl['\xc5benraa'] = "That was a unicode key."
|
||||||
|
@ -128,40 +128,32 @@ class TestPlistlib(unittest.TestCase):
|
||||||
pl2 = plistlib.readPlist(test_support.TESTFN)
|
pl2 = plistlib.readPlist(test_support.TESTFN)
|
||||||
self.assertEqual(dict(pl), dict(pl2))
|
self.assertEqual(dict(pl), dict(pl2))
|
||||||
|
|
||||||
def test_string(self):
|
def test_bytes(self):
|
||||||
pl = self._create()
|
pl = self._create()
|
||||||
data = plistlib.writePlistToString(pl)
|
data = plistlib.writePlistToBytes(pl)
|
||||||
pl2 = plistlib.readPlistFromString(data)
|
pl2 = plistlib.readPlistFromBytes(data)
|
||||||
self.assertEqual(dict(pl), dict(pl2))
|
self.assertEqual(dict(pl), dict(pl2))
|
||||||
data2 = plistlib.writePlistToString(pl2)
|
data2 = plistlib.writePlistToBytes(pl2)
|
||||||
self.assertEqual(data, data2)
|
self.assertEqual(data, data2)
|
||||||
|
|
||||||
def test_appleformatting(self):
|
def test_appleformatting(self):
|
||||||
pl = plistlib.readPlistFromString(TESTDATA)
|
pl = plistlib.readPlistFromBytes(TESTDATA)
|
||||||
data = plistlib.writePlistToString(pl)
|
data = plistlib.writePlistToBytes(pl)
|
||||||
self.assertEqual(data, TESTDATA,
|
self.assertEqual(data, TESTDATA,
|
||||||
"generated data was not identical to Apple's output")
|
"generated data was not identical to Apple's output")
|
||||||
|
|
||||||
def test_appleformattingfromliteral(self):
|
def test_appleformattingfromliteral(self):
|
||||||
pl = self._create()
|
pl = self._create()
|
||||||
pl2 = plistlib.readPlistFromString(TESTDATA)
|
pl2 = plistlib.readPlistFromBytes(TESTDATA)
|
||||||
self.assertEqual(dict(pl), dict(pl2),
|
self.assertEqual(dict(pl), dict(pl2),
|
||||||
"generated data was not identical to Apple's output")
|
"generated data was not identical to Apple's output")
|
||||||
|
|
||||||
def test_stringio(self):
|
def test_bytesio(self):
|
||||||
from StringIO import StringIO
|
from io import BytesIO
|
||||||
f = StringIO()
|
b = BytesIO()
|
||||||
pl = self._create()
|
pl = self._create()
|
||||||
plistlib.writePlist(pl, f)
|
plistlib.writePlist(pl, b)
|
||||||
pl2 = plistlib.readPlist(StringIO(f.getvalue()))
|
pl2 = plistlib.readPlist(BytesIO(b.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()))
|
|
||||||
self.assertEqual(dict(pl), dict(pl2))
|
self.assertEqual(dict(pl), dict(pl2))
|
||||||
|
|
||||||
def test_controlcharacters(self):
|
def test_controlcharacters(self):
|
||||||
|
@ -170,17 +162,17 @@ class TestPlistlib(unittest.TestCase):
|
||||||
testString = "string containing %s" % c
|
testString = "string containing %s" % c
|
||||||
if i >= 32 or c in "\r\n\t":
|
if i >= 32 or c in "\r\n\t":
|
||||||
# \r, \n and \t are the only legal control chars in XML
|
# \r, \n and \t are the only legal control chars in XML
|
||||||
plistlib.writePlistToString(testString)
|
plistlib.writePlistToBytes(testString)
|
||||||
else:
|
else:
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
plistlib.writePlistToString,
|
plistlib.writePlistToBytes,
|
||||||
testString)
|
testString)
|
||||||
|
|
||||||
def test_nondictroot(self):
|
def test_nondictroot(self):
|
||||||
test1 = "abc"
|
test1 = "abc"
|
||||||
test2 = [1, 2, 3, "abc"]
|
test2 = [1, 2, 3, "abc"]
|
||||||
result1 = plistlib.readPlistFromString(plistlib.writePlistToString(test1))
|
result1 = plistlib.readPlistFromBytes(plistlib.writePlistToBytes(test1))
|
||||||
result2 = plistlib.readPlistFromString(plistlib.writePlistToString(test2))
|
result2 = plistlib.readPlistFromBytes(plistlib.writePlistToBytes(test2))
|
||||||
self.assertEqual(test1, result1)
|
self.assertEqual(test1, result1)
|
||||||
self.assertEqual(test2, result2)
|
self.assertEqual(test2, result2)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue