web-dev-qa-db-fra.com

Comment faire des appels futurs et attendre la fin avec Python?

J'ai le code suivant où j'ai une liste de noms d'utilisateurs et j'essaie de vérifier si les utilisateurs sont dans un groupe d'utilisateurs Windows spécifique en utilisant Net User \domain | find somegroup.

Le problème est que je lance cette commande pour environ 8 groupes d'utilisateurs par nom d'utilisateur, ce qui est lent. Je voudrais envoyer ces appels en utilisant des futures et même des threads séparés (si cela le rend plus rapide).

Je dois juste attendre à la fin avant de faire autre chose. Comment puis-je m'y prendre en 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
15
orange

(Cette réponse ignore actuellement le code HTML. L'analyse de votre code ne ... vous pouvez la mettre dans un pool de la même manière que cette approche met en attente les appels Net User)}

Commençons par définir une fonction qui prend une Tuple de (user, group) et renvoie les informations souhaitées.

# 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)

Maintenant, nous pouvons l’exécuter dans un pool de threads en utilisant 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()

La results est une liste de (user, group, data) tuples et peut être traitée à votre guise.

Remarque: Ce code est actuellement non testé en raison d'une différence de plate-forme

10
donkopotamus

En python 3, une solution plus simple et plus pratique consiste à utiliser concurrent.futures.

Le module concurrent.futures fournit une interface de haut niveau pour l'exécution des appelables de manière asynchrone. Référence...

import concurrent.futures


# Get a list containing all groups of a user
def get_groups(username):
    # Do the request and check here
    # And return the groups of current user with a list
    return list()

with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Mark each future with its groups
    future_to_groups = {executor.submit(get_groups, user): user
                        for user in user_list}

    # Now it comes to the result of each user
    for future in concurrent.futures.as_completed(future_to_groups):
        user = future_to_groups[future]
        try:
            # Receive the returned result of current user
            groups = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (user, exc))
        else:
            # Here you do anything you need on `groups`
            # Output or collect them
            print('%r is in %d groups' % (user, len(groups)))

Notez que max_workers signifie ici le nombre maximal de threads.

Voir ici d'où provient cet exemple.

MODIFIER: 

Si vous devez faire chaque vérification dans un fil séparé:

import concurrent.futures


# Check if a `user` is in a `group`
def check(user, group):
    # Do the check here
    # And return True if user is in this group, False if not
    return True

with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Mark each future with its user and group
    future_to_checks = {executor.submit(check, user, group): (user, group)
                        for user in user_list for group in group_list}

    # Now it comes to the result of each check
    # The try-except-else clause is omitted here
    for future in concurrent.futures.as_completed(future_to_checks):
        user, group = future_to_checks[future]
        in_group = future.result()
        if in_group is True:
            print('%r is in %r' % (user, group))

Inspiré par @donkopotamus, itertools.product pourrait être utilisé ici pour générer toutes les cibles.

Et si vous n'avez pas besoin de gérer les exceptions, ce serait beaucoup plus simple:

import concurrent.futures
from itertools import product
from collections import defaultdict


def check(target):
    user, group = target
    return True

with concurrent.futures.ThreadPoolExecutor() as executor:
    results = defaultdict(list)
    targets = list(product(user_list, group_list))
    for (user, group), in_group in Zip(targets, executor.map(check, targets)):
        if in_group is True:
            results[user].append(group)

    print(results)
2
Lodour

Il semble que problème du consommateur producteur

Le fil principal devrait générer les tâches 

class Task:
    def Task(self,user,group)
        self.user  = user
        self.group = group
    def run(self):
        pass # call command with self.user and self.group and process results

twp = TaskWorkerPool(4)
for group in groups:
    twp.add( Task(user,group) )
twp.wait()
1
napuzba