plistlib – Manipulate OS X property list files

Purpose:Read and write OS X property list files
Available In:2.6

plistlib provides an interface for working with property list files used under OS X. plist files are typically XML, sometimes compressed. They are used by the operating system and applications to store preferences or other configuration settings. The contents are usually structured as a dictionary containing key value pairs of basic built-in types (unicode strings, integers, dates, etc.). Values can also be nested data structures such as other dictionaries or lists. Binary data, or strings with control characters, can be encoded using the data type.

Reading plist Files

OS X applications such as iCal use plist files to store meta-data about objects they manage. For example, iCal stores the definitions of all of your calendars as a series of plist files in the Library directory.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>AlarmFilter</key>
	<true/>
	<key>AlarmsDisabled</key>
	<false/>
	<key>AttachmentFilter</key>
	<true/>
	<key>AutoRefresh</key>
	<true/>
	<key>Checked</key>
	<integer>1</integer>
	<key>Color</key>
	<string>#808000FF</string>
	<key>Enabled</key>
	<true/>
	<key>Key</key>
	<string>4221BCE5-1017-4EE4-B7FF-311A846C600D</string>
	<key>NeedsForcedUpdate</key>
	<false/>
	<key>NeedsRefresh</key>
	<true/>
	<key>Order</key>
	<integer>25</integer>
	<key>RefreshDate</key>
	<date>2009-11-29T16:31:53Z</date>
	<key>RefreshInterval</key>
	<integer>3600</integer>
	<key>SubscriptionTitle</key>
	<string>Athens, GA Weather - By Weather Underground</string>
	<key>SubscriptionURL</key>
	<string>http://ical.wunderground.com/auto/ical/GA/Athens.ics?units=both</string>
	<key>TaskFilter</key>
	<true/>
	<key>Title</key>
	<string>Athens, GA Weather - By Weather Underground</string>
	<key>Type</key>
	<string>Subscription</string>
</dict>
</plist>

This sample script finds the calendar defintions, reads them, and prints the titles of any calendars being displayed by iCal (having the property Checked set to a true value).

import plistlib
import os
import glob

calendar_root = os.path.expanduser('~/Library/Calendars')
calendar_directories = (
    glob.glob(os.path.join(calendar_root, '*.caldav', '*.calendar')) +
    glob.glob(os.path.join(calendar_root, '*.calendar'))
    )

for dirname in calendar_directories:
    info_filename = os.path.join(dirname, 'Info.plist')
    if os.path.isfile(info_filename):
        info = plistlib.readPlist(info_filename)
        if info.get('Checked'):
            print info['Title']

The type of the Checked property is defined by the plist file, so our script does not need to convert the string to an integer.

$ python plistlib_checked_calendars.py
Doug Hellmann
Tasks
Vacation Schedule
EarthSeasons
US Holidays
Athens, GA Weather - By Weather Underground
Birthdays
Georgia Bulldogs Calendar (NCAA Football)
Home
Meetup: Django
Meetup: Python

Writing plist Files

If you want to use plist files to save your own settings, use writePlist() to serialize the data and write it to the filesystem.

import plistlib
import datetime
import tempfile

d = { 'an_int':2,
      'a_bool':False,
      'the_float':5.9,
      'simple_string':'This string has no special characters.',
      'xml_string':'<element attr="value">This string includes XML markup &nbsp;</element>',
      'nested_list':['a', 'b', 'c'],
      'nested_dict':{ 'key':'value' },
      'timestamp':datetime.datetime.now(),
      }

output_file = tempfile.NamedTemporaryFile()
try:
    plistlib.writePlist(d, output_file)
    output_file.seek(0)
    print output_file.read()
finally:
    output_file.close()
    

The first argument is the data structure to write out, and the second is an open file handle or the name of a file.

$ python plistlib_write_plist.py

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>a_bool</key>
        <false/>
        <key>an_int</key>
        <integer>2</integer>
        <key>nested_dict</key>
        <dict>
                <key>key</key>
                <string>value</string>
        </dict>
        <key>nested_list</key>
        <array>
                <string>a</string>
                <string>b</string>
                <string>c</string>
        </array>
        <key>simple_string</key>
        <string>This string has no special characters.</string>
        <key>the_float</key>
        <real>5.9</real>
        <key>timestamp</key>
        <date>2013-02-21T06:36:30Z</date>
        <key>xml_string</key>
        <string>&lt;element attr="value"&gt;This string includes XML markup &amp;nbsp;&lt;/element&gt;</string>
</dict>
</plist>

Binary Property Data

Serializing binary data or strings that may include control characters using a plist is not immune to the typical challenges for an XML format. To work around the issues, plist files can store binary data in base64 format if the object is wrapped with a Data instance.

import plistlib

d = { 'binary_data':plistlib.Data('This data has an embedded null. \0'),
      }

print plistlib.writePlistToString(d)

This example uses the writePlistToString() to create an in-memory string, instead of writing to a file.

$ python plistlib_binary_write.py

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>binary_data</key>
        <data>
        VGhpcyBkYXRhIGhhcyBhbiBlbWJlZGRlZCBudWxsLiAA
        </data>
</dict>
</plist>

Binary data is automatically converted to a Data instance when read.

import plistlib
import pprint

DATA = """<?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">
<plist version="1.0">
<dict>
        <key>binary_data</key>
        <data>
        VGhpcyBkYXRhIGhhcyBhbiBlbWJlZGRlZCBudWxsLiAA
        </data>
</dict>
</plist>
"""

d = plistlib.readPlistFromString(DATA)

print repr(d['binary_data'].data)

The data attribute of the object contains the decoded data.

$ python plistlib_binary_read.py

'This data has an embedded null. \x00'

See also

plistlib
The standard library documentation for this module.
plist manual page
Documentation of the plist file format.
Weather Underground
Free weather information, including ICS and RSS feeds.
Convert plist between XML and Binary formats
Some plist files are stored in a binary format instead of XML because the binary format is faster to parse using Apple’s libraries. Python’s plistlib module does not handle the binary format, so you may need to convert binary files to XML using plutil before reading them.
Using Python for System Administration
Presentation from Nigel Kersten and Chris Adams, including details of using PyObjC to load plists using the native Cocoa API, which transparently handles both the XML and binary formats. See slice 27, especially.