def leopardRemoveWireless(networkName):
plistPath = '/Library/Preferences/SystemConfiguration/preferences.plist'
# Sanity checks for the plist
if os.path.exists(plistPath):
try:
pl = NSMutableDictionary.dictionaryWithContentsOfFile_(plistPath)
except:
print 'Unable to parse file at path: %s' % plistPath
sys.exit(1)
else:
print 'File does not exist at path: %s' % plistPath
sys.exit(1)
# Create a copy of the dictionary due to emuration
copy = NSDictionary.dictionaryWithDictionary_(pl)
# Iterate through network sets
for Set in copy['Sets']:
UserDefinedName = copy['Sets'][Set]['UserDefinedName']
print 'Processing location: %s' % UserDefinedName
for enX in copy['Sets'][Set]['Network']['Interface']:
print 'Processing interface: %s' % enX
# I think this will always be a single key but this works either way
for key in copy['Sets'][Set]['Network']['Interface'][enX]:
print 'Processing Service: %s' % key
# Try to grab the PreferredNetworks key if any
try:
# Iterate through preferred network sets
index = 0
for PreferredNetwork in copy['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks']:
SSID_STR = PreferredNetwork['SSID_STR']
print 'Processing SSID: %s' % SSID_STR
# If the preferred network matches our removal SSID
if SSID_STR == networkName:
print 'Found SSID %s to remove' % SSID_STR
# Delete our in ram copy
print 'Processing Set: %s' % Set
print 'Processing enX: %s' % enX
print 'Processing key: %s' % key
del pl['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks'][index]
index += 1
except KeyError:
print 'Skipping interface without PreferredNetworks'
I am editing a fairly complex (dictionary) plist and then writing the changes back to the file after I find a specific key value pair. The issue is that even though I am making a copy of the Property lists dictionary:
copy = NSDictionary.dictionaryWithDictionary_(pl)
It is giving me the standard "mutated while enumerated" error when I edit the original, just the loop keys as stand ins (notice the lack of quotes).
del pl['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks'][index]
Is my syntax somehow causing it to try and edit the pl
dictionary rather then the copy
? Here is the output:
... Processing Set: D5C0A0F4-613A-4121-B6AE-4CBA6E2635FF Processing enX: en1 Processing key: AirPort Traceback (most recent call last): File "/Users/tester/Desktop/wifiutil", line 1164, in sys.exit(main()) File "/Users/tester/Desktop/wifiutil", line 1135, in main removeWireless(osVersion,network) File "/Users/tester/Desktop/wifiutil", line 1051, in removeWireless leopardRemoveWireless(network)
File "/Users/tester/Desktop/wifiutil", line 528, in leopardRemoveWireless for PreferredNetwork in copy['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks']: File "/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC/objc/_convenience.py", line 431, in enumeratorGenerator yield container_unwrap(anEnumerator.nextObject(), StopIteration) objc.error: NSGenericException - * Collection was mutated while being enumerated.
I believe that the issue is the nested nature of the dictionary. dictionaryWithDictionary_()
doesn't do anything like a deep copy; all it does is create a new NSDictionary
and copy the pointers for the values (it does copy the keys themselves, since that is the nature of NSDictionary
).
This means that, although you have a new top-level which you can use for enumeration, the inner dictionaries and arrays are the exact same objects as are in the original.
Your last loop:
for PreferredNetwork in copy['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks']:
is enumerating one of those inner arrays, which you then try to mutate with a del
statement:
del pl['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks'][index]
This wasn't copied; it is the same array object that you are using in the for
, which causes an exception. You can test this by passing the two expressions to id()
.
You'll have to either do a full-depth copy of the original dictionary, or (probably better) take a copy of the last level before you enumerate it.