patternpythonMinor
Find installed and available Windows Updates
Viewed 0 times
availablewindowsfindandinstalledupdates
Problem
I have a function that uses Windows Update Agent (WUA) API through COM using the win32com.client module, part of pywin32 package for retrieving the installed and the available (not yet installed) Windows Updates. For each update, name, url of the relative Microsoft documentation and category is displayed. Finally the function returns 2 dictionaries containing a name and category of the installed updates and the available updates (not yet installed).
This is the code I wrote, it compiles and produces the expected result:
```
import win32com.client
import win32con
import win32api
import pywintypes
import re
def enum_winupdates():
wua = win32com.client.Dispatch("Microsoft.Update.Session")
update_seeker = wua.CreateUpdateSearcher()
# Search installed Software Windows Updates
search_installed = update_seeker.Search("IsInstalled=1 and Type='Software'")
updates_installed = win32com.client.Dispatch("Microsoft.Update.UpdateColl")
print("\n[+] Enumerating installed Windows or Drivers' Updates...(if any)\n")
installed_updates = []
installed_categories = []
available_updates = []
available_categories = []
installed_dict = {}
available_dict = {}
# compiles the regex pattern for finding Windows Update codes
updates_pattern = re.compile(r'KB+\d+')
for i in range(0, (len(search_installed.Updates))):
# saves installed update name in a variable
update_installed = search_installed.Updates.Item(i)
for j in range(0, len(update_installed.Categories)):
# extracts Windows Update code using regex
update_code = updates_pattern.findall(str(update_installed))
# saves installed update category in a variable
category = update_installed.Categories.Item(j).Name
print("[*] Name: " + str(update_installed) + " - " +
"url: " + "https://support.microsoft.com/en-us/kb/{}".format(
"".join(update_code).strip("KB")) + " - " +
This is the code I wrote, it compiles and produces the expected result:
```
import win32com.client
import win32con
import win32api
import pywintypes
import re
def enum_winupdates():
wua = win32com.client.Dispatch("Microsoft.Update.Session")
update_seeker = wua.CreateUpdateSearcher()
# Search installed Software Windows Updates
search_installed = update_seeker.Search("IsInstalled=1 and Type='Software'")
updates_installed = win32com.client.Dispatch("Microsoft.Update.UpdateColl")
print("\n[+] Enumerating installed Windows or Drivers' Updates...(if any)\n")
installed_updates = []
installed_categories = []
available_updates = []
available_categories = []
installed_dict = {}
available_dict = {}
# compiles the regex pattern for finding Windows Update codes
updates_pattern = re.compile(r'KB+\d+')
for i in range(0, (len(search_installed.Updates))):
# saves installed update name in a variable
update_installed = search_installed.Updates.Item(i)
for j in range(0, len(update_installed.Categories)):
# extracts Windows Update code using regex
update_code = updates_pattern.findall(str(update_installed))
# saves installed update category in a variable
category = update_installed.Categories.Item(j).Name
print("[*] Name: " + str(update_installed) + " - " +
"url: " + "https://support.microsoft.com/en-us/kb/{}".format(
"".join(update_code).strip("KB")) + " - " +
Solution
Loop like a native
I suggest you have a look/read at Ned Batchelder's excellent presentation "Loop like a native".
In your case, any time you write:
You should most probably write:
If you are using objects that do not support this natively, it may be worth writting an adapter as suggested in Raymond Hettinger's "Beyond PEP 8" talk.
(In any case, please note that
Do less, less often
You perform this operation
Also, you call
Do not repeat yourself
It seems that you are doing the same thing twice. Only a few parameters are different. You could define a function to handle this.
At this stage, the code looks like (and is highly untested, the variable names might be pretty bad and I may have introduced errors but you'll get the idea of what I was trying to achieve):
Do less (again)
From the code above, it is clear that elements from
Also, I expect the
You could simply write:
Proper string format
In :
You use a mix of string concatenation of string formatting. This is highly confusing. Instead, you could simply write:
Do things less often (again)
I missed it in the first place but you could do the join-and-strip part out of the loop:
Data structure
I have troubles understanding what the returns data structure is supposed to be. A dictionnary mapping each category to a single update is a bit weird
I suggest you have a look/read at Ned Batchelder's excellent presentation "Loop like a native".
In your case, any time you write:
for i in range(0, (len(search_installed.Updates))):
# saves installed update name in a variable
update_installed = search_installed.Updates.Item(i)You should most probably write:
for update_installed in search_installed.Updates:
# saves installed update name in a variableIf you are using objects that do not support this natively, it may be worth writting an adapter as suggested in Raymond Hettinger's "Beyond PEP 8" talk.
(In any case, please note that
0 as a first argument of range is useless, you can write range(foo) instead of range(0, foo))Do less, less often
You perform this operation
update_code = updates_pattern.findall(str(update_installed)) for each category even though the result will (as far as I can tell) not change from one category to another. It would probably make sense to do this out of the loop.Also, you call
str(update_installed) many times for the same update_installed. Again, it may be worth doing only once.Do not repeat yourself
It seems that you are doing the same thing twice. Only a few parameters are different. You could define a function to handle this.
At this stage, the code looks like (and is highly untested, the variable names might be pretty bad and I may have introduced errors but you'll get the idea of what I was trying to achieve):
import win32com.client
import win32con
import win32api
import pywintypes
import re
def get_software_updates(update_seeker, installed):
# Search installed/not installed Software Windows Updates
search_string = "IsInstalled=%d and Type='Software'" % installed
search_update = update_seeker.Search(search_string)
_ = win32com.client.Dispatch("Microsoft.Update.UpdateColl")
updates = []
categories = []
update_dict = {}
# compiles the regex pattern for finding Windows Update codes
updates_pattern = re.compile(r'KB+\d+')
for update in search_update.Updates:
update_str = str(update)
# extracts Windows Update code using regex
update_code = updates_pattern.findall(update_str)
for category in update.Categories:
category_name = category.Name
print("[*] Name: " + update_str + " - " +
"url: " + "https://support.microsoft.com/en-us/kb/{}".format(
"".join(update_code).strip("KB")) + " - " +
"Category: " + category_name)
updates.append(update_str)
categories.append(category_name)
# converts lists to tuples in order to be used as a dictionary key
hashable = tuple(updates)
hashable_category = tuple(categories)
# creates category:update dictionary
for update in hashable:
for category_update in hashable_category:
update_dict[category_update] = str(update)
return update_dict
def enum_winupdates():
wua = win32com.client.Dispatch("Microsoft.Update.Session")
update_seeker = wua.CreateUpdateSearcher()
print("\n[+] Enumerating installed Windows or Drivers' Updates...(if any)\n")
installed = get_software_updates(update_seeker, installed=True)
print("\n[+] Enumerating available Windows or Drivers' Updates not installed...(if any)\n")
available = get_software_updates(update_seeker, installed=False)
return installed, availableDo less (again)
From the code above, it is clear that elements from
updates are already string but we have still calling str on them. I expect this to be useless.Also, I expect the
hashable and hashable_categories tuples not to be useful.You could simply write:
# creates category:update dictionary
for update in updates:
for category_update in categories:
update_dict[category_update] = update
return update_dictProper string format
In :
print("[*] Name: " + update_str + " - " +
"url: " + "https://support.microsoft.com/en-us/kb/{}".format(
"".join(update_code).strip("KB")) + " - " +
"Category: " + category_name)You use a mix of string concatenation of string formatting. This is highly confusing. Instead, you could simply write:
print("[*] Name: {} - url: https://support.microsoft.com/en-us/kb/{} - Category: {}".format(
update_str,
"".join(update_code).strip("KB"),
category_name))Do things less often (again)
I missed it in the first place but you could do the join-and-strip part out of the loop:
# extracts Windows Update code using regex
update_codes = updates_pattern.findall(update_str)
update_codes_stripped = "".join(update_code).strip("KB")Data structure
I have troubles understanding what the returns data structure is supposed to be. A dictionnary mapping each category to a single update is a bit weird
Code Snippets
for i in range(0, (len(search_installed.Updates))):
# saves installed update name in a variable
update_installed = search_installed.Updates.Item(i)for update_installed in search_installed.Updates:
# saves installed update name in a variableimport win32com.client
import win32con
import win32api
import pywintypes
import re
def get_software_updates(update_seeker, installed):
# Search installed/not installed Software Windows Updates
search_string = "IsInstalled=%d and Type='Software'" % installed
search_update = update_seeker.Search(search_string)
_ = win32com.client.Dispatch("Microsoft.Update.UpdateColl")
updates = []
categories = []
update_dict = {}
# compiles the regex pattern for finding Windows Update codes
updates_pattern = re.compile(r'KB+\d+')
for update in search_update.Updates:
update_str = str(update)
# extracts Windows Update code using regex
update_code = updates_pattern.findall(update_str)
for category in update.Categories:
category_name = category.Name
print("[*] Name: " + update_str + " - " +
"url: " + "https://support.microsoft.com/en-us/kb/{}".format(
"".join(update_code).strip("KB")) + " - " +
"Category: " + category_name)
updates.append(update_str)
categories.append(category_name)
# converts lists to tuples in order to be used as a dictionary key
hashable = tuple(updates)
hashable_category = tuple(categories)
# creates category:update dictionary
for update in hashable:
for category_update in hashable_category:
update_dict[category_update] = str(update)
return update_dict
def enum_winupdates():
wua = win32com.client.Dispatch("Microsoft.Update.Session")
update_seeker = wua.CreateUpdateSearcher()
print("\n[+] Enumerating installed Windows or Drivers' Updates...(if any)\n")
installed = get_software_updates(update_seeker, installed=True)
print("\n[+] Enumerating available Windows or Drivers' Updates not installed...(if any)\n")
available = get_software_updates(update_seeker, installed=False)
return installed, available# creates category:update dictionary
for update in updates:
for category_update in categories:
update_dict[category_update] = update
return update_dictprint("[*] Name: " + update_str + " - " +
"url: " + "https://support.microsoft.com/en-us/kb/{}".format(
"".join(update_code).strip("KB")) + " - " +
"Category: " + category_name)Context
StackExchange Code Review Q#135648, answer score: 9
Revisions (0)
No revisions yet.