Python Forum

Full Version: Accessing nested dictionary values. Plistlib, Python 2.7
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2 3 4
Hi Guys.

I am try to adapt a piece of code I found on Github to help me extract even more data from the .spx files that macOS's System Profiler uses to store its data (using a variant of xml).

I can use the code already given as well as my own to extract the serial number and other basic info for Apple machinery. I would also like to get at more detailed information such as the size of the hard drive and graphics card info. These are a bit 'deeper' in the XML structure and thus appear to be in the form of nested dictionaries. I would like help getting at these!

Here is some code...

[b]#!/usr/bin/python
# https://gist.github.com/pudquick/83ec0ce0a5f3dbc52e70c8c1ce64a389
# orher link https://github.com/chilcote/pylab/blob/master/pysystemprofiler.py1
# python 2.7.11[/b]

import plistlib, sys
import os
import pyperclip

# Grab the path to the spx file (it is a command line arg.)
file_path = sys.argv[1]

# ingest the spx (which is a plist)
file_data = plistlib.readPlist(file_path)


candidates = []
candidates2 = []

# retrieve the basic HW info such as serial, RAM amount, machine type etc....
for x in file_data:
    if x.get('_parentDataType', None) == 'SPRootDataType':
        if x.get('_dataType', None) == 'SPHardwareDataType':
            candidates.append(x)

# retrieve other info related specifically to the SATA drives...
for y in file_data:
    if y.get('_parentDataType', None) == 'SPHardwareDataType':
        if y.get('_dataType', None) == 'SPSerialATADataType':
            candidates2.append(y)


# this all works fine for me
for x in candidates:
    pyperclip.copy(x['_items'][0]['machine_model'])
    print x['_items'][0]['machine_model']
    print x['_items'][0]['cpu_type']
    print x['_items'][0]['current_processor_speed']
    print x['_items'][0]['serial_number']
    print x['_items'][0]['physical_memory']

#problems here...
for y in candidates2:
    print y['_items'][0]['_name'] #outputs OK, returns ''Apple SSD Controller'
    print y['_items'][0][''size'] # always returns "KeyError: 'size_in_bytes'"
For the initial list (SPHardwareDataType) there are no "nested" lists or dics involved. Hence why it's so easy to get a value for each of the strings.

I've run
for y in candidates2:
    print y['_items'][0]
and have gotten back

_items: [{'spsata_vendor': 'Apple', '_name': 'Apple SSD Controller', g: 'SSD Controller', 'spsata_physical_interconnect': 'PCI', 'spsata_linkspeed': '8.0 GT/s', 'spsata_linkwidth': 'x4', 'spsata_portdescription': 'AHCI Version 1.30 Supported', '_items': [{'smart_status': 'Verified', 'detachable_drive': 'no', 'spsata_ncq_depth': '32', 'device_model': 'APPLE SSD SM0512G                       ', 'spsata_trim_support': 'Yes', 'size_in_bytes': 500277790720, '_name': 'APPLE SSD SM0512G', 'spsata_ncq': 'Yes', 'bsd_name': 'disk0', 'device_revision': 'BXW1SA0Q', 'volumes': [{'size_in_bytes': 209715200, 'iocontent': 'EFI', '_name': 'EFI', 'bsd_name': 'disk0s1', 'volume_uuid': '0E239BC6-F960-3107-89CF-1C97F78BB46B', 'file_system': 'MS-DOS FAT32', 'size': '209.7 MB'}, {'mount_point': '/', 'size_in_bytes': 499418034176, 'iocontent': 'Apple_HFS', 'free_space': '135.88 GB', 'writable': 'yes', 'bsd_name': 'disk0s2', 'volume_uuid': '9CFCBC78-FA5D-3F60-8D04-F8572178AE9C', '_name': 'Mac HD', 'free_space_in_bytes': 135877349376, 'file_system': 'Journaled HFS+', 'size': '499.42 GB'}, {'size_in_bytes': 650002432, 'iocontent': 'Apple_Boot', '_name': 'Recovery HD', 'bsd_name': 'disk0s3', 'volume_uuid': '40B348A9-1201-3064-9B94-55A6A0D74FB5', 'file_system': 'Journaled HFS+', 'size': '650 MB'}], 'spsata_medium_type': 'Solid State', 'device_serial': 'S29ANYAG493962      ', 'removable_media': 'no', 'partition_map_type': 'guid_partition_map_type', 'size': '500.28 GB'}]}]
A lot to take in!

I hope that's not too awkward to read on one line.

I would be amazed if someone could show me the command to get to get to the keys and values at deeper levels of 'SPSerialATADataType'

The value I would like to able to read most is the 'size'. It has a value of '499.42 GB' in the feedback I payed above.

Thanks again.

William

Here is a screenshot of a section of the .spx file's XML structure that you help you figure out what I am trying to achieve.

[Image: GMbcelO.jpg]

Here is the link otherwise, if it not rendering on the page....
xml structure of apple system profiler spx file
I can retrieve a value for "_name" easily but its the 'size' of the mac's internal hard drive that I really want to return.

Thanks.
My question was perhaps too verbose on SO and received no feedback.

I rephrased things here to make it as python-centric as possible. I've also deleted the post (from several days ago) on SO, just for your information. Thanks.
try
 
for y in candidates2:
    for volume in y['_items'][0]['volumes']:
        print volume['size']
@buran

Thanks for trying to help me out.

Just give your suggestion a go. Still got KeyError: 'volumes'

Is it possible to approach this like it is in the first 'for' loop? Where the basic hardware info is retrieved and printed out.

Thanks again.
sorry, there was one more _items in it, that I missed in this long line
for y in candidates2:
    for item1 in y['_items']:
        for item2 in item1:
            for volume in item2['volumes']:
                print volume['size']
Thanks again for your efforts.

Getting a TypeError: string indices must be integers, not str error this time. I think the plislib module likes to make life tough for us all!

This might help you out though...

The outputs of...
print (type(file_data))
print (type(candidates))
print (type(y))

...

<type 'list'>
<type 'list'>
<class 'plistlib._InternalDict'>
can you upload the sdx file somewhere like github so I can test
cool. will pm you.
post the link here. so other members could help too.
by the way what python version do you use? because plistlib.readPlist is depreciated since 3.4. That is not the cause of the problem here, but should be addressed anyway
Pages: 1 2 3 4