Copied plistlib.py from r60150 Lib/plat-mac/plistlib.py to Lib/

This commit is contained in:
Christian Heimes 2008-01-26 22:09:42 +00:00
parent 6ff93fee5f
commit 4f110d8805

View file

@ -1,6 +1,6 @@
"""plistlib.py -- a tool to generate and parse MacOSX .plist files.
The PropertyList (.plist) file format is a simple XML pickle supporting
The PropertList (.plist) file format is a simple XML pickle supporting
basic object types, like dictionaries, lists, numbers and strings.
Usually the top level object is a dictionary.
@ -12,8 +12,8 @@
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,24 +21,24 @@
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:
pl = dict(
aString="Doodah",
aList=["A", "B", 12, 32.1, [1, 2, 3]],
aFloat=0.1,
anInt=728,
aFloat = 0.1,
anInt = 728,
aDict=dict(
anotherString="<hello & hi there!>",
aUnicodeValue=u'M\xe4ssig, Ma\xdf',
aTrueValue=True,
aFalseValue=False,
),
someData=Data("<binary gunk>"),
someMoreData=Data("<lots of binary gunk>" * 10),
aDate=datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
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:
pl[u'\xc5benraa'] = "That was a unicode key."
@ -52,7 +52,7 @@
__all__ = [
"readPlist", "writePlist", "readPlistFromString", "writePlistToString",
"readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes",
"readPlistFromResource", "writePlistToResource",
"Plist", "Data", "Dict"
]
@ -60,7 +60,7 @@
import binascii
import datetime
from io import StringIO
from io import BytesIO
import re
@ -69,10 +69,10 @@ def readPlist(pathOrFile):
(readable) file object. Return the unpacked root object (which
usually is a dictionary).
"""
didOpen = 0
didOpen = False
if isinstance(pathOrFile, str):
pathOrFile = open(pathOrFile)
didOpen = 1
pathOrFile = open(pathOrFile, 'rb')
didOpen = True
p = PlistParser()
rootObject = p.parse(pathOrFile)
if didOpen:
@ -84,10 +84,10 @@ def writePlist(rootObject, pathOrFile):
"""Write 'rootObject' to a .plist file. 'pathOrFile' may either be a
file name or a (writable) file object.
"""
didOpen = 0
didOpen = False
if isinstance(pathOrFile, str):
pathOrFile = open(pathOrFile, "w")
didOpen = 1
pathOrFile = open(pathOrFile, 'wb')
didOpen = True
writer = PlistWriter(pathOrFile)
writer.writeln("<plist version=\"1.0\">")
writer.writeValue(rootObject)
@ -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 = []
@ -165,16 +164,19 @@ def endElement(self, element):
def simpleElement(self, element, value=None):
if value is not None:
value = _escapeAndEncode(value)
value = _escape(value)
self.writeln("<%s>%s</%s>" % (element, value, element))
else:
self.writeln("<%s/>" % element)
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(b'\n')
# Contents should conform to a subset of ISO 8601
@ -205,7 +207,7 @@ def _dateToString(d):
r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]")
def _escapeAndEncode(text):
def _escape(text):
m = _controlCharPat.search(text)
if m is not None:
raise ValueError("strings can't contains control characters; "
@ -215,17 +217,17 @@ def _escapeAndEncode(text):
text = text.replace("&", "&amp;") # escape '&'
text = text.replace("<", "&lt;") # escape '<'
text = text.replace(">", "&gt;") # escape '>'
return text.encode("utf-8") # encode as UTF-8
return text
PLISTHEADER = """\
PLISTHEADER = 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">
"""
class PlistWriter(DumbXMLWriter):
def __init__(self, file, indentLevel=0, indent="\t", writeHeader=1):
def __init__(self, file, indentLevel=0, indent=b"\t", writeHeader=1):
if writeHeader:
file.write(PLISTHEADER)
DumbXMLWriter.__init__(self, file, indentLevel, indent)
@ -258,9 +260,9 @@ def writeValue(self, value):
def writeData(self, data):
self.beginElement("data")
self.indentLevel -= 1
maxlinelength = 76 - len(self.indent.replace("\t", " " * 8) *
maxlinelength = 76 - len(self.indent.replace(b"\t", b" " * 8) *
self.indentLevel)
for line in data.asBase64(maxlinelength).split("\n"):
for line in data.asBase64(maxlinelength).split(b"\n"):
if line:
self.writeln(line)
self.indentLevel += 1
@ -268,8 +270,7 @@ def writeData(self, data):
def writeDict(self, d):
self.beginElement("dict")
items = list(d.items())
items.sort()
items = sorted(d.items())
for key, value in items:
if not isinstance(key, str):
raise TypeError("keys must be strings")
@ -321,7 +322,7 @@ def __init__(self, **kwargs):
from warnings import warn
warn("The plistlib.Dict class is deprecated, use builtin dict instead",
PendingDeprecationWarning)
super(Dict, self).__init__(**kwargs)
super().__init__(**kwargs)
class Plist(_InternalDict):
@ -334,7 +335,7 @@ def __init__(self, **kwargs):
from warnings import warn
warn("The Plist class is deprecated, use the readPlist() and "
"writePlist() functions instead", PendingDeprecationWarning)
super(Plist, self).__init__(**kwargs)
super().__init__(**kwargs)
def fromFile(cls, pathOrFile):
"""Deprecated. Use the readPlist() function instead."""
@ -356,31 +357,33 @@ 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
@classmethod
def fromBase64(cls, data):
# base64.decodestring just calls binascii.a2b_base64;
# it seems overkill to use both base64 and binascii.
return cls(binascii.a2b_base64(data))
fromBase64 = classmethod(fromBase64)
def asBase64(self, maxlinelength=76):
return _encodeBase64(self.data, maxlinelength)
def __cmp__(self, other):
def __eq__(self, other):
if isinstance(other, self.__class__):
return cmp(self.data, other.data)
return self.data == other.data
elif isinstance(other, str):
return cmp(self.data, other)
return self.data == other
else:
return cmp(id(self), id(other))
return id(self) == id(other)
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, repr(self.data))
@ -427,11 +430,7 @@ def addObject(self, value):
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
@ -465,6 +464,6 @@ def end_real(self):
def end_string(self):
self.addObject(self.getData())
def end_data(self):
self.addObject(Data.fromBase64(self.getData()))
self.addObject(Data.fromBase64(self.getData().encode("utf-8")))
def end_date(self):
self.addObject(_dateFromString(self.getData()))