#!/usr/bin/python # encoding: utf-8 # # Copyright 2009-2016 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """FoundationPlist.py -- a tool to generate and parse OS X .plist files. This is intended as a drop-in replacement for Python's included plistlib, with a few caveats: - readPlist() and writePlist() operate only on a filepath, not a file object. - there is no support for the deprecated functions: readPlistFromResource() writePlistToResource() - there is no support for the deprecated Plist class. The Property List (.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. To write out a plist file, use the writePlist(rootObject, filepath) function. 'rootObject' is the top level object, 'filepath' is a filename. To parse a plist from a file, use the readPlist(filepath) function, with a file name. It returns the top level object (again, usually a dictionary). To work with plist data in strings, you can use readPlistFromString() and writePlistToString(). """ # PyLint cannot properly find names inside Cocoa libraries, so issues bogus # No name 'Foo' in module 'Bar' warnings. Disable them. # pylint: disable=E0611 from Foundation import NSData from Foundation import NSPropertyListSerialization from Foundation import NSPropertyListMutableContainers from Foundation import NSPropertyListXMLFormat_v1_0 # pylint: enable=E0611 # Disable PyLint complaining about 'invalid' camelCase names # pylint: disable=C0103 class FoundationPlistException(Exception): """Basic exception for plist errors""" pass class NSPropertyListSerializationException(FoundationPlistException): """Read/parse error for plists""" pass class NSPropertyListWriteException(FoundationPlistException): """Write error for plists""" pass def readPlist(filepath): """ Read a .plist file from filepath. Return the unpacked root object (which is usually a dictionary). """ plistData = NSData.dataWithContentsOfFile_(filepath) dataObject, dummy_plistFormat, error = ( NSPropertyListSerialization. propertyListFromData_mutabilityOption_format_errorDescription_( plistData, NSPropertyListMutableContainers, None, None)) if dataObject is None: if error: error = error.encode('ascii', 'ignore') else: error = "Unknown error" errmsg = "%s in file %s" % (error, filepath) raise NSPropertyListSerializationException(errmsg) else: return dataObject def readPlistFromString(data): '''Read a plist data from a string. Return the root object.''' try: plistData = buffer(data) except TypeError, err: raise NSPropertyListSerializationException(err) dataObject, dummy_plistFormat, error = ( NSPropertyListSerialization. propertyListFromData_mutabilityOption_format_errorDescription_( plistData, NSPropertyListMutableContainers, None, None)) if dataObject is None: if error: error = error.encode('ascii', 'ignore') else: error = "Unknown error" raise NSPropertyListSerializationException(error) else: return dataObject def writePlist(dataObject, filepath): ''' Write 'rootObject' as a plist to filepath. ''' plistData, error = ( NSPropertyListSerialization. dataFromPropertyList_format_errorDescription_( dataObject, NSPropertyListXMLFormat_v1_0, None)) if plistData is None: if error: error = error.encode('ascii', 'ignore') else: error = "Unknown error" raise NSPropertyListSerializationException(error) else: if plistData.writeToFile_atomically_(filepath, True): return else: raise NSPropertyListWriteException( "Failed to write plist data to %s" % filepath) def writePlistToString(rootObject): '''Return 'rootObject' as a plist-formatted string.''' plistData, error = ( NSPropertyListSerialization. dataFromPropertyList_format_errorDescription_( rootObject, NSPropertyListXMLFormat_v1_0, None)) if plistData is None: if error: error = error.encode('ascii', 'ignore') else: error = "Unknown error" raise NSPropertyListSerializationException(error) else: return str(plistData)