Question

I'm trying to modify a plist file using python. I copied a plist from my library to my desktop to safely play with it. I've imported os and plistlib. I'm following what I see in the documentation here.

import plistlib
import os

test_prefs = "~/Desktop/com.apple.Safari.plist"

x = readPlist(os.path.expanduser(test_prefs))
print x["TopSitesGridArrangement"]

But this fails. What am I doing wrong?

The exact error I get:

Traceback (most recent call last):
  File "/Users/Josh/Desktop/destroy.py", line 11, in <module>
    x = readPlist(os.path.expanduser(test_prefs))
NameError: name 'readPlist' is not defined

When I change it to x = plistlib.readPlist(os.path.expanduser(test_prefs)) the errors I get are as follows (my file name is called destroy.py):

Traceback (most recent call last):
  File "/Users/Josh/Desktop/destroy.py", line 11, in <module>
    x = plistlib.readPlist(os.path.expanduser(test_prefs))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plistlib.py", line 78, in readPlist
    rootObject = p.parse(pathOrFile)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plistlib.py", line 406, in parse
    parser.ParseFile(fileobj)
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 8
Was it helpful?

Solution

The issue is that Safari's plist file is actually a binary plist file format, which the built-in plistlib can't read. However, biplist can read these files (requires installation):

>>> import biplist
>>> x = biplist.readPlist("com.apple.Safari.plist")
>>> x['LastOSVersionSafariWasLaunchedOn']
'10.9.1'

Alternatively, you can use plutil to first convert the binary plist format to xml format, and then read it using plistlib:

$ plutil -convert xml1 com.apple.Safari.plist
$ python
>>> import plistlib
>>> x = plistlib.readPlist("com.apple.Safari.plist")
>>> x['LastOSVersionSafariWasLaunchedOn']
'10.9.1'

OTHER TIPS

Shouldn't it be x = plistlib.readPlist(os.path.expanduser(test_prefs))?

This line:

import plistlib

Creates a namespace plistlib, in which all its objects are stored, in order to keep from clobbering your own variable names.

To access the function readPlist, you need to use the dot-notation to access plistlib.readPlist:

x = plistlib.readPlist(os.path.expanduser(test_prefs))

As an alternative, you can use this syntax to bring what you need into your global space:

from plistlib import readPlist, writePlist  # and anything else...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top