How to make future calls and wait until complete with Python?

orange picture orange · Apr 22, 2017 · Viewed 9k times · Source

I have the following code where I have a list of usernames and I try and check if the users are in a specific Windows Usergroup using net user \domain | find somegroup.

The problem is that I run that command for about 8 usergroups per username and it is slow. I would like to send off these calls using futures and even separate threads (if it makes it quicker).

I just have to wait at the end before i do anything else. How do I go about doing it in Python?

for one_username in user_list:
    response = requests.get(somecontent)

    bs_parsed = BeautifulSoup(response.content, 'html.parser')

    find_all2 = bs_parsed.find("div", {"class": "QuickLinks"})
    name = re.sub("\s\s+", ' ', find_all2.find("td", text="Name").find_next_sibling("td").text)

    find_all = bs_parsed.find_all("div", {"class": "visible"})
    all_perms = ""
    d.setdefault(one_username + " (" + name + ")", [])
    for value in find_all:
        test = value.find("a", {"onmouseover": True})
        if test is not None:
            if "MyAppID" in test.text:
                d[one_username + " (" + name + ")"].append(test.text)

    for group in groups:
        try:
            d[one_username + " (" + name + ")"].append(check_output("net user /domain " + one_username + "| find \"" + group + "\"", shell=True, stderr=subprocess.STDOUT).strip().decode("utf-8"))
        except Exception:
            pass

Answer

donkopotamus picture donkopotamus · Apr 25, 2017

(This answer currently ignores HTML parsing your code does ... you can queue that into a pool identically to how this approach queues the net user calls)

First, lets define a function that takes a tuple of (user, group) and returns the desired information.

# a function that calls net user to find info on a (user, group)
def get_group_info(usr_grp):
    # unpack the arguments
    usr, grp = usr_grp

    try:
        return (usr, grp, 
                check_output(
                    "net user /domain " + usr + "| find \"" + grp + "\"", 
                    shell=True, 
                    stderr=subprocess.STDOUT
                    ).strip().decode("utf-8")))
    except Exception:
        return (usr, grp, None)

Now, we can run this in a thread pool using multiprocessing.dummy.Pool

from multiprocessing.dummy import Pool
import itertools

# create a pool with four worker threads
pool = Pool(4)

# run get_group_info for every user, group
async_result = pool.map_async(get_group_info, itertools.product(user_list, groups))

# now do some other work we care about
...

# and then wait on our results
results = async_result.get()

The results are a list of (user, group, data) tuples and can be processed as you desire.

Note: This code is currently untested due to a difference in platforms