symbian-qemu-0.9.1-12/python-win32-2.6.1/lib/plistlib.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """plistlib.py -- a tool to generate and parse MacOSX .plist files.
       
     2 
       
     3 The PropertyList (.plist) file format is a simple XML pickle supporting
       
     4 basic object types, like dictionaries, lists, numbers and strings.
       
     5 Usually the top level object is a dictionary.
       
     6 
       
     7 To write out a plist file, use the writePlist(rootObject, pathOrFile)
       
     8 function. 'rootObject' is the top level object, 'pathOrFile' is a
       
     9 filename or a (writable) file object.
       
    10 
       
    11 To parse a plist from a file, use the readPlist(pathOrFile) function,
       
    12 with a file name or a (readable) file object as the only argument. It
       
    13 returns the top level object (again, usually a dictionary).
       
    14 
       
    15 To work with plist data in strings, you can use readPlistFromString()
       
    16 and writePlistToString().
       
    17 
       
    18 Values can be strings, integers, floats, booleans, tuples, lists,
       
    19 dictionaries, Data or datetime.datetime objects. String values (including
       
    20 dictionary keys) may be unicode strings -- they will be written out as
       
    21 UTF-8.
       
    22 
       
    23 The <data> plist type is supported through the Data class. This is a
       
    24 thin wrapper around a Python string.
       
    25 
       
    26 Generate Plist example:
       
    27 
       
    28     pl = dict(
       
    29         aString="Doodah",
       
    30         aList=["A", "B", 12, 32.1, [1, 2, 3]],
       
    31         aFloat=0.1,
       
    32         anInt=728,
       
    33         aDict=dict(
       
    34             anotherString="<hello & hi there!>",
       
    35             aUnicodeValue=u'M\xe4ssig, Ma\xdf',
       
    36             aTrueValue=True,
       
    37             aFalseValue=False,
       
    38         ),
       
    39         someData=Data("<binary gunk>"),
       
    40         someMoreData=Data("<lots of binary gunk>" * 10),
       
    41         aDate=datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
       
    42     )
       
    43     # unicode keys are possible, but a little awkward to use:
       
    44     pl[u'\xc5benraa'] = "That was a unicode key."
       
    45     writePlist(pl, fileName)
       
    46 
       
    47 Parse Plist example:
       
    48 
       
    49     pl = readPlist(pathOrFile)
       
    50     print pl["aKey"]
       
    51 """
       
    52 
       
    53 
       
    54 __all__ = [
       
    55     "readPlist", "writePlist", "readPlistFromString", "writePlistToString",
       
    56     "readPlistFromResource", "writePlistToResource",
       
    57     "Plist", "Data", "Dict"
       
    58 ]
       
    59 # Note: the Plist and Dict classes have been deprecated.
       
    60 
       
    61 import binascii
       
    62 import datetime
       
    63 from cStringIO import StringIO
       
    64 import re
       
    65 import warnings
       
    66 
       
    67 
       
    68 def readPlist(pathOrFile):
       
    69     """Read a .plist file. 'pathOrFile' may either be a file name or a
       
    70     (readable) file object. Return the unpacked root object (which
       
    71     usually is a dictionary).
       
    72     """
       
    73     didOpen = 0
       
    74     if isinstance(pathOrFile, (str, unicode)):
       
    75         pathOrFile = open(pathOrFile)
       
    76         didOpen = 1
       
    77     p = PlistParser()
       
    78     rootObject = p.parse(pathOrFile)
       
    79     if didOpen:
       
    80         pathOrFile.close()
       
    81     return rootObject
       
    82 
       
    83 
       
    84 def writePlist(rootObject, pathOrFile):
       
    85     """Write 'rootObject' to a .plist file. 'pathOrFile' may either be a
       
    86     file name or a (writable) file object.
       
    87     """
       
    88     didOpen = 0
       
    89     if isinstance(pathOrFile, (str, unicode)):
       
    90         pathOrFile = open(pathOrFile, "w")
       
    91         didOpen = 1
       
    92     writer = PlistWriter(pathOrFile)
       
    93     writer.writeln("<plist version=\"1.0\">")
       
    94     writer.writeValue(rootObject)
       
    95     writer.writeln("</plist>")
       
    96     if didOpen:
       
    97         pathOrFile.close()
       
    98 
       
    99 
       
   100 def readPlistFromString(data):
       
   101     """Read a plist data from a string. Return the root object.
       
   102     """
       
   103     return readPlist(StringIO(data))
       
   104 
       
   105 
       
   106 def writePlistToString(rootObject):
       
   107     """Return 'rootObject' as a plist-formatted string.
       
   108     """
       
   109     f = StringIO()
       
   110     writePlist(rootObject, f)
       
   111     return f.getvalue()
       
   112 
       
   113 
       
   114 def readPlistFromResource(path, restype='plst', resid=0):
       
   115     """Read plst resource from the resource fork of path.
       
   116     """
       
   117     warnings.warnpy3k("In 3.x, readPlistFromResource is removed.")
       
   118     from Carbon.File import FSRef, FSGetResourceForkName
       
   119     from Carbon.Files import fsRdPerm
       
   120     from Carbon import Res
       
   121     fsRef = FSRef(path)
       
   122     resNum = Res.FSOpenResourceFile(fsRef, FSGetResourceForkName(), fsRdPerm)
       
   123     Res.UseResFile(resNum)
       
   124     plistData = Res.Get1Resource(restype, resid).data
       
   125     Res.CloseResFile(resNum)
       
   126     return readPlistFromString(plistData)
       
   127 
       
   128 
       
   129 def writePlistToResource(rootObject, path, restype='plst', resid=0):
       
   130     """Write 'rootObject' as a plst resource to the resource fork of path.
       
   131     """
       
   132     warnings.warnpy3k("In 3.x, writePlistToResource is removed.")
       
   133     from Carbon.File import FSRef, FSGetResourceForkName
       
   134     from Carbon.Files import fsRdWrPerm
       
   135     from Carbon import Res
       
   136     plistData = writePlistToString(rootObject)
       
   137     fsRef = FSRef(path)
       
   138     resNum = Res.FSOpenResourceFile(fsRef, FSGetResourceForkName(), fsRdWrPerm)
       
   139     Res.UseResFile(resNum)
       
   140     try:
       
   141         Res.Get1Resource(restype, resid).RemoveResource()
       
   142     except Res.Error:
       
   143         pass
       
   144     res = Res.Resource(plistData)
       
   145     res.AddResource(restype, resid, '')
       
   146     res.WriteResource()
       
   147     Res.CloseResFile(resNum)
       
   148 
       
   149 
       
   150 class DumbXMLWriter:
       
   151 
       
   152     def __init__(self, file, indentLevel=0, indent="\t"):
       
   153         self.file = file
       
   154         self.stack = []
       
   155         self.indentLevel = indentLevel
       
   156         self.indent = indent
       
   157 
       
   158     def beginElement(self, element):
       
   159         self.stack.append(element)
       
   160         self.writeln("<%s>" % element)
       
   161         self.indentLevel += 1
       
   162 
       
   163     def endElement(self, element):
       
   164         assert self.indentLevel > 0
       
   165         assert self.stack.pop() == element
       
   166         self.indentLevel -= 1
       
   167         self.writeln("</%s>" % element)
       
   168 
       
   169     def simpleElement(self, element, value=None):
       
   170         if value is not None:
       
   171             value = _escapeAndEncode(value)
       
   172             self.writeln("<%s>%s</%s>" % (element, value, element))
       
   173         else:
       
   174             self.writeln("<%s/>" % element)
       
   175 
       
   176     def writeln(self, line):
       
   177         if line:
       
   178             self.file.write(self.indentLevel * self.indent + line + "\n")
       
   179         else:
       
   180             self.file.write("\n")
       
   181 
       
   182 
       
   183 # Contents should conform to a subset of ISO 8601
       
   184 # (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'.  Smaller units may be omitted with
       
   185 #  a loss of precision)
       
   186 _dateParser = re.compile(r"(?P<year>\d\d\d\d)(?:-(?P<month>\d\d)(?:-(?P<day>\d\d)(?:T(?P<hour>\d\d)(?::(?P<minute>\d\d)(?::(?P<second>\d\d))?)?)?)?)?Z")
       
   187 
       
   188 def _dateFromString(s):
       
   189     order = ('year', 'month', 'day', 'hour', 'minute', 'second')
       
   190     gd = _dateParser.match(s).groupdict()
       
   191     lst = []
       
   192     for key in order:
       
   193         val = gd[key]
       
   194         if val is None:
       
   195             break
       
   196         lst.append(int(val))
       
   197     return datetime.datetime(*lst)
       
   198 
       
   199 def _dateToString(d):
       
   200     return '%04d-%02d-%02dT%02d:%02d:%02dZ' % (
       
   201         d.year, d.month, d.day,
       
   202         d.hour, d.minute, d.second
       
   203     )
       
   204 
       
   205 
       
   206 # Regex to find any control chars, except for \t \n and \r
       
   207 _controlCharPat = re.compile(
       
   208     r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
       
   209     r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]")
       
   210 
       
   211 def _escapeAndEncode(text):
       
   212     m = _controlCharPat.search(text)
       
   213     if m is not None:
       
   214         raise ValueError("strings can't contains control characters; "
       
   215                          "use plistlib.Data instead")
       
   216     text = text.replace("\r\n", "\n")       # convert DOS line endings
       
   217     text = text.replace("\r", "\n")         # convert Mac line endings
       
   218     text = text.replace("&", "&amp;")       # escape '&'
       
   219     text = text.replace("<", "&lt;")        # escape '<'
       
   220     text = text.replace(">", "&gt;")        # escape '>'
       
   221     return text.encode("utf-8")             # encode as UTF-8
       
   222 
       
   223 
       
   224 PLISTHEADER = """\
       
   225 <?xml version="1.0" encoding="UTF-8"?>
       
   226 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
       
   227 """
       
   228 
       
   229 class PlistWriter(DumbXMLWriter):
       
   230 
       
   231     def __init__(self, file, indentLevel=0, indent="\t", writeHeader=1):
       
   232         if writeHeader:
       
   233             file.write(PLISTHEADER)
       
   234         DumbXMLWriter.__init__(self, file, indentLevel, indent)
       
   235 
       
   236     def writeValue(self, value):
       
   237         if isinstance(value, (str, unicode)):
       
   238             self.simpleElement("string", value)
       
   239         elif isinstance(value, bool):
       
   240             # must switch for bool before int, as bool is a
       
   241             # subclass of int...
       
   242             if value:
       
   243                 self.simpleElement("true")
       
   244             else:
       
   245                 self.simpleElement("false")
       
   246         elif isinstance(value, (int, long)):
       
   247             self.simpleElement("integer", "%d" % value)
       
   248         elif isinstance(value, float):
       
   249             self.simpleElement("real", repr(value))
       
   250         elif isinstance(value, dict):
       
   251             self.writeDict(value)
       
   252         elif isinstance(value, Data):
       
   253             self.writeData(value)
       
   254         elif isinstance(value, datetime.datetime):
       
   255             self.simpleElement("date", _dateToString(value))
       
   256         elif isinstance(value, (tuple, list)):
       
   257             self.writeArray(value)
       
   258         else:
       
   259             raise TypeError("unsuported type: %s" % type(value))
       
   260 
       
   261     def writeData(self, data):
       
   262         self.beginElement("data")
       
   263         self.indentLevel -= 1
       
   264         maxlinelength = 76 - len(self.indent.replace("\t", " " * 8) *
       
   265                                  self.indentLevel)
       
   266         for line in data.asBase64(maxlinelength).split("\n"):
       
   267             if line:
       
   268                 self.writeln(line)
       
   269         self.indentLevel += 1
       
   270         self.endElement("data")
       
   271 
       
   272     def writeDict(self, d):
       
   273         self.beginElement("dict")
       
   274         items = d.items()
       
   275         items.sort()
       
   276         for key, value in items:
       
   277             if not isinstance(key, (str, unicode)):
       
   278                 raise TypeError("keys must be strings")
       
   279             self.simpleElement("key", key)
       
   280             self.writeValue(value)
       
   281         self.endElement("dict")
       
   282 
       
   283     def writeArray(self, array):
       
   284         self.beginElement("array")
       
   285         for value in array:
       
   286             self.writeValue(value)
       
   287         self.endElement("array")
       
   288 
       
   289 
       
   290 class _InternalDict(dict):
       
   291 
       
   292     # This class is needed while Dict is scheduled for deprecation:
       
   293     # we only need to warn when a *user* instantiates Dict or when
       
   294     # the "attribute notation for dict keys" is used.
       
   295 
       
   296     def __getattr__(self, attr):
       
   297         try:
       
   298             value = self[attr]
       
   299         except KeyError:
       
   300             raise AttributeError, attr
       
   301         from warnings import warn
       
   302         warn("Attribute access from plist dicts is deprecated, use d[key] "
       
   303              "notation instead", PendingDeprecationWarning)
       
   304         return value
       
   305 
       
   306     def __setattr__(self, attr, value):
       
   307         from warnings import warn
       
   308         warn("Attribute access from plist dicts is deprecated, use d[key] "
       
   309              "notation instead", PendingDeprecationWarning)
       
   310         self[attr] = value
       
   311 
       
   312     def __delattr__(self, attr):
       
   313         try:
       
   314             del self[attr]
       
   315         except KeyError:
       
   316             raise AttributeError, attr
       
   317         from warnings import warn
       
   318         warn("Attribute access from plist dicts is deprecated, use d[key] "
       
   319              "notation instead", PendingDeprecationWarning)
       
   320 
       
   321 class Dict(_InternalDict):
       
   322 
       
   323     def __init__(self, **kwargs):
       
   324         from warnings import warn
       
   325         warn("The plistlib.Dict class is deprecated, use builtin dict instead",
       
   326              PendingDeprecationWarning)
       
   327         super(Dict, self).__init__(**kwargs)
       
   328 
       
   329 
       
   330 class Plist(_InternalDict):
       
   331 
       
   332     """This class has been deprecated. Use readPlist() and writePlist()
       
   333     functions instead, together with regular dict objects.
       
   334     """
       
   335 
       
   336     def __init__(self, **kwargs):
       
   337         from warnings import warn
       
   338         warn("The Plist class is deprecated, use the readPlist() and "
       
   339              "writePlist() functions instead", PendingDeprecationWarning)
       
   340         super(Plist, self).__init__(**kwargs)
       
   341 
       
   342     def fromFile(cls, pathOrFile):
       
   343         """Deprecated. Use the readPlist() function instead."""
       
   344         rootObject = readPlist(pathOrFile)
       
   345         plist = cls()
       
   346         plist.update(rootObject)
       
   347         return plist
       
   348     fromFile = classmethod(fromFile)
       
   349 
       
   350     def write(self, pathOrFile):
       
   351         """Deprecated. Use the writePlist() function instead."""
       
   352         writePlist(self, pathOrFile)
       
   353 
       
   354 
       
   355 def _encodeBase64(s, maxlinelength=76):
       
   356     # copied from base64.encodestring(), with added maxlinelength argument
       
   357     maxbinsize = (maxlinelength//4)*3
       
   358     pieces = []
       
   359     for i in range(0, len(s), maxbinsize):
       
   360         chunk = s[i : i + maxbinsize]
       
   361         pieces.append(binascii.b2a_base64(chunk))
       
   362     return "".join(pieces)
       
   363 
       
   364 class Data:
       
   365 
       
   366     """Wrapper for binary data."""
       
   367 
       
   368     def __init__(self, data):
       
   369         self.data = data
       
   370 
       
   371     def fromBase64(cls, data):
       
   372         # base64.decodestring just calls binascii.a2b_base64;
       
   373         # it seems overkill to use both base64 and binascii.
       
   374         return cls(binascii.a2b_base64(data))
       
   375     fromBase64 = classmethod(fromBase64)
       
   376 
       
   377     def asBase64(self, maxlinelength=76):
       
   378         return _encodeBase64(self.data, maxlinelength)
       
   379 
       
   380     def __cmp__(self, other):
       
   381         if isinstance(other, self.__class__):
       
   382             return cmp(self.data, other.data)
       
   383         elif isinstance(other, str):
       
   384             return cmp(self.data, other)
       
   385         else:
       
   386             return cmp(id(self), id(other))
       
   387 
       
   388     def __repr__(self):
       
   389         return "%s(%s)" % (self.__class__.__name__, repr(self.data))
       
   390 
       
   391 
       
   392 class PlistParser:
       
   393 
       
   394     def __init__(self):
       
   395         self.stack = []
       
   396         self.currentKey = None
       
   397         self.root = None
       
   398 
       
   399     def parse(self, fileobj):
       
   400         from xml.parsers.expat import ParserCreate
       
   401         parser = ParserCreate()
       
   402         parser.StartElementHandler = self.handleBeginElement
       
   403         parser.EndElementHandler = self.handleEndElement
       
   404         parser.CharacterDataHandler = self.handleData
       
   405         parser.ParseFile(fileobj)
       
   406         return self.root
       
   407 
       
   408     def handleBeginElement(self, element, attrs):
       
   409         self.data = []
       
   410         handler = getattr(self, "begin_" + element, None)
       
   411         if handler is not None:
       
   412             handler(attrs)
       
   413 
       
   414     def handleEndElement(self, element):
       
   415         handler = getattr(self, "end_" + element, None)
       
   416         if handler is not None:
       
   417             handler()
       
   418 
       
   419     def handleData(self, data):
       
   420         self.data.append(data)
       
   421 
       
   422     def addObject(self, value):
       
   423         if self.currentKey is not None:
       
   424             self.stack[-1][self.currentKey] = value
       
   425             self.currentKey = None
       
   426         elif not self.stack:
       
   427             # this is the root object
       
   428             self.root = value
       
   429         else:
       
   430             self.stack[-1].append(value)
       
   431 
       
   432     def getData(self):
       
   433         data = "".join(self.data)
       
   434         try:
       
   435             data = data.encode("ascii")
       
   436         except UnicodeError:
       
   437             pass
       
   438         self.data = []
       
   439         return data
       
   440 
       
   441     # element handlers
       
   442 
       
   443     def begin_dict(self, attrs):
       
   444         d = _InternalDict()
       
   445         self.addObject(d)
       
   446         self.stack.append(d)
       
   447     def end_dict(self):
       
   448         self.stack.pop()
       
   449 
       
   450     def end_key(self):
       
   451         self.currentKey = self.getData()
       
   452 
       
   453     def begin_array(self, attrs):
       
   454         a = []
       
   455         self.addObject(a)
       
   456         self.stack.append(a)
       
   457     def end_array(self):
       
   458         self.stack.pop()
       
   459 
       
   460     def end_true(self):
       
   461         self.addObject(True)
       
   462     def end_false(self):
       
   463         self.addObject(False)
       
   464     def end_integer(self):
       
   465         self.addObject(int(self.getData()))
       
   466     def end_real(self):
       
   467         self.addObject(float(self.getData()))
       
   468     def end_string(self):
       
   469         self.addObject(self.getData())
       
   470     def end_data(self):
       
   471         self.addObject(Data.fromBase64(self.getData()))
       
   472     def end_date(self):
       
   473         self.addObject(_dateFromString(self.getData()))