Как сделать будущие звонки и подождать до завершения с Python?
У меня есть следующий код, где у меня есть список имен пользователей, и я пытаюсь проверить, находятся ли пользователи в определенной группе Windows, используя net user \domain | find somegroup
.
Проблема заключается в том, что я запускаю эту команду для примерно 8 пользовательских групп для каждого имени пользователя, и она медленная. Я хотел бы отправить эти вызовы с использованием фьючерсов и даже отдельных потоков (если это ускорится).
Мне просто нужно подождать в конце, прежде чем я сделаю что-нибудь еще. Как мне это сделать в 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
Ответы
Ответ 1
(Этот ответ в настоящее время игнорирует HTML-анализ вашего кода... вы можете поставить очередь в пул идентично тому, как этот подход останавливает вызовы net user
)
Сначала давайте определим функцию, которая принимает tuple
из (user, group)
и возвращает требуемую информацию.
# 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)
Теперь мы можем запустить это в пуле потоков, используя 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()
results
- это список кортежей (user, group, data)
и может обрабатываться по вашему желанию.
Примечание: Этот код в настоящее время не проверен из-за различий в платформах
Ответ 2
Кажется, что проблема производителя.
Основной поток должен генерировать задачи
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()
Ответ 3
В python 3 более простым и удобным решением является использование concurrent.futures
.
Модуль concurrent.futures
предоставляет высокоуровневый интерфейс для асинхронного выполнения вызовов. Ссылка...
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)))
Обратите внимание, что max_workers
здесь означает максимальное количество потоков.
Смотрите здесь, откуда этот пример.
ИЗМЕНИТЬ:
Если вам нужно выполнить каждую проверку в отдельном потоке:
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))
Вдохновленный @donkopotamus, itertools.product
можно использовать здесь для создания всех целей.
И если вам не нужно обрабатывать исключения, это было бы намного проще:
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)