I have created a program that contains two main async functions that I run with asyncio:
- get_all_group_members(): takes a list of Entra ID group IDs and returns a list of members of that group by using the Microsoft Graph API (using a function in another script)
- get_user_name(): takes a list of users who have access to an Azure resource and gets the display name for the user using the Microsoft Graph API
If I run my program once, with one list of Entra ID groups and one list of Azure resource users, everything works just fine, I get the results I expect from the Graph API. However, if I then put my code into a loop that performs the same actions for multiple lists of groups and multiple lists of Azure resource users, it will fail only on the second iteration of the loop with error Event loop is closed
. I get this error on the second iteration of the loop no matter which methodology I use with asyncio.
I want to know how I can perform separate Graph API async calls with asyncio and get the second loop of API calls to work. I need to be able to loop through a list of resources/groups and get data for all of them, not just run one group/list at a time.
What I’ve tried (none of these work for me):
- Using asyncio.gather() and then asyncio.run()
async def get_all_group_members():
memberListAll = []
for group in groupList:
results = await gq.get_group_user(group['GroupID'], graphServiceClient)
memberDict = {"GroupName":group["GroupName"], "GroupID":group["GroupID"], "MemberList":results}
memberListAll.append(memberDict)
return memberListAll
# This function will get the display name for an Entra ID user based on the object ID for that account
async def get_user_name():
ownerUsernameList = []
for owner in ownersList:
if owner['principalType'] == 'User':
results = await gq.get_user_by_id(userID=owner['principalID'],graphServiceClient=graphServiceClient)
ownerUsernameList.append(results)
return ownerUsernameList
async def run_async_functions():
groupResults, usernameResults = await asyncio.gather(get_all_group_members(), get_user_name())
return groupResults, usernameResults
groupResults, usernameResults = asyncio.run(run_async_functions())
- Creating my own Loop, opening the Loop, using the Loop, then closing the Loop. On the second iteration of the processing for my lists/calling the Graph API, my Loop will say it’s open yet I’ll still get the above error when it runs the async functions.
asyncio.set_event_loop(asyncio.new_event_loop())
newLoop = asyncio.get_event_loop()
isOpen = asyncio.get_event_loop().is_closed()
print("Is the loop open? ", not isOpen)
>>>> Is the loop open? True
async def get_all_group_members():
memberListAll = []
for group in groupList:
results = await gq.get_group_user(group['GroupID'], graphServiceClient) # Calls function from other script I wrote
memberDict = {"GroupName":group["GroupName"], "GroupID":group["GroupID"], "MemberList":results}
memberListAll.append(memberDict)
return memberListAll
async def get_user_name():
ownerUsernameList = []
for owner in ownersList:
if owner['principalType'] == 'User':
results = await gq.get_user_by_id(userID=owner['principalID'],graphServiceClient=graphServiceClient)
ownerUsernameList.append(results)
return ownerUsernameList
# This function calls both of the above async functions so that we can have one function for asyncio to run
async def run_async_functions():
groupResults = await get_all_group_members()
usernameResults = await get_user_name()
return groupResults, usernameResults
groupResults, usernameResults = newLoop.run_until_complete(run_async_functions())
newLoop.close()
isClosed = asyncio.get_event_loop().is_closed()
print("Is the loop closed? ", str(isClosed))
>>>> Is the loop closed? True
- Creating a separate loop for the first async function, closing that loop, then creating a second Loop for the second async function. Yet I still get a failure with the same error, but only on the second iteration of my script when it’s processing the second list of groups/users.
async def get_all_group_members():
memberListAll = []
for group in groupList:
results = await gq.get_group_user(group['GroupID'], graphServiceClient)
memberDict = {"GroupName":group["GroupName"], "GroupID":group["GroupID"], "MemberList":results}
memberListAll.append(memberDict)
return memberListAll
async def get_user_name():
ownerUsernameList = []
for owner in ownersList:
if owner['principalType'] == 'User':
results = await gq.get_user_by_id(userID=owner['principalID'],graphServiceClient=graphServiceClient)
ownerUsernameList.append(results)
return ownerUsernameList
groupLoop = asyncio.new_event_loop()
asyncio.set_event_loop(groupLoop)
isOpen = asyncio.get_event_loop().is_closed()
print("Is the loop open? ", not isOpen)
>>>> Is the loop open? True
groupResults = groupLoop.run_until_complete(get_all_group_members())
groupLoop.close()
isClosed = asyncio.get_event_loop().is_closed()
print("Is the loop closed? ", str(isClosed))
>>>> Is the loop closed? True
usernameLoop = asyncio.new_event_loop()
asyncio.set_event_loop(usernameLoop)
isOpen = asyncio.get_event_loop().is_closed()
print("Is the loop open? ", not isOpen)
>>>> Is the loop open? True
usernameResults = usernameLoop.run_until_complete(get_user_name())
usernameLoop.close()
isClosed = asyncio.get_event_loop().is_closed()
print("Is the loop closed? ", str(isClosed))
>>>> Is the loop closed? True
So how do I successfully get these async functions to work and not error out on the second iteration of my program when I try to process the second list of groups and users? Why, even though I specifically open a loop and it says it is open, do I get an “event loop is closed” error?