Creating the parse_device_info() function
This function interprets the raw string from the setupapi.dev.log and converts it into a dictionary with VID, PID, revision, unique ID, and date values. This is described in the docstring on lines 94 through 98. After the documentation, we initialize the variables we will use in this function on lines 101 through 104. This initialization provides default placeholder values, which will prevent future issues with the dictionary in scenarios where we cannot assign a value to these variables:
092 def parse_device_info(device_info):
093 """
094 Parses Vendor, Product, Revision and UID from a Setup API
095 entry
096 :param device_info: string of device information to parse
097 :return: dictionary of parsed information or original string
098 if error
099 """
100 # Initialize variables
101 vid = ''
102 pid = ''
103 rev = ''
104 uid = ''
After initialization, we split the device_info value, which is passed from the parse_setup_api() function into segments, using a single backslash as the delimiter. We need to escape this backslash with another to interpret it as a literal backslash character. This split on line 107 separates the device type segment from the string containing the VID and PID information. Following this split, we check to ensure that the device type entry reflects a USB device. If the device is not a USB, we return None to ensure that it is not processed further by this function and that we do not attempt to resolve VIDs or PIDs for this device. By adding this logic, we save ourselves from spending additional time and resources processing irrelevant entries:
106 # Split string into segments on \
107 segments = device_info[0].split('\\')
108
109 if 'usb' not in segments[0].lower():
110 return None
Next, we access the second element of the segments list, which contains the VID, PID, and revision data, delimited by an ampersand. Using .split(), we can access each of these values independently through the for loop on line 114. We convert the line to lower case to allow us to search in a case-insensitive fashion, through a series of conditionals, to determine what each item represents. On line 116, we check each item to see if it contains the keywords ven or vid. If the line does contain one of these indicators, we split only on the first underscore character (specified by the integer 1 as the second parameter). This allows us to extract the VID from the raw string. Note how we use lower_item for our comparisons and the item variable for storing values, preserving the original case of our data. This behavior is repeated for the pid variable, using the dev, prod, and pid indicators, and the rev variable, using the rev or mi indicators on lines 118 through 122, as follows:
114 for item in segments[1].split('&'):
115 lower_item = item.lower()
116 if 'ven' in lower_item or 'vid' in lower_item:
117 vid = item.split('_', 1)[-1]
118 elif 'dev' in lower_item or 'pid' in lower_item or \
119 'prod' in lower_item:
120 pid = item.split('_', 1)[-1]
121 elif 'rev' in lower_item or 'mi' in lower_item:
122 rev = item.split('_', 1)[-1]
After parsing the VID, PID, and revision information, we attempt to extract the unique ID from the segments variable, which is normally the last element in the string. Because the entire line is wrapped in brackets, we strip the closing bracket from the rightmost entry in the segment on line 125. This removes the bracket, so it will not be included in our unique ID string:
124 if len(segments) >= 3:
125 uid = segments[2].strip(']')
On line 127, we use an if statement to determine whether the vid or pid received a value after initialization, and build a dictionary if we collected new information on lines 128 through 132. If these values were not filled out, we return the original string to allow the output of the entry without the additional formatting, as seen on line 134, to ensure that we are not missing any data due to a formatting error:
127 if vid != '' or pid != '':
128 return {'Vendor ID': vid.lower(),
129 'Product ID': pid.lower(),
130 'Revision': rev,
131 'UID': uid,
132 'First Installation Date': device_info[1]}
133 # Unable to parse data, returning whole string
134 return device_info