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
1 changed files with 49 additions and 50 deletions

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 @@ 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,24 +21,24 @@ 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:
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 @@ 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 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 @@ class DumbXMLWriter:
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 @@ _controlCharPat = re.compile(
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 @@ class PlistWriter(DumbXMLWriter):
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 @@ class PlistWriter(DumbXMLWriter):
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 @@ class Dict(_InternalDict):
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 @@ class Plist(_InternalDict):
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 @@ 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
@ -465,6 +464,6 @@ class PlistParser:
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()))