본문 바로가기

Python

(에러)RuntimeError: Task <Task pending name='Task-1' coro=<update_one() running at test.py:7> cb=[_run_until_complete_cb()

from bson.objectid import ObjectId
from motor.motor_asyncio import AsyncIOMotorClient
import asyncio
a = AsyncIOMotorClient(mongourl)
async def update_one():
    print("complete")
    return await a[database][collection].insert_one({"_id": ObjectId("61bc40f123a9a9ac317d51c5")},{"a":"b"})
asyncio.run(update_one())

위와 같이 asyncio.run을 시키자 아래와 같은 에러가 발생했다.

과연 이러한 에러는 무엇일까?

 

일단 구글링 한 결과

loop = asyncio.get_event_loop()
loop.run_until_complete(update_one())

이렇게 get_event_loop를 사용하면 실행이 잘된다.

그렇다면 get_event_loop()와 run()의 차이는 무엇일까?

 

https://github.com/awestlake87/pyo3-asyncio/issues/19

 

Task got Future <Future pending> attached to a different loop · Issue #19 · awestlake87/pyo3-asyncio

I'm trying to reproduce the simplest example with Python native module with async function exported and call it from Python. lib.rs: use std::time::Duration; use pyo3::{prelude::*, wrap_pyfunct...

github.com

여기 issue에서 힌트를 얻을 수 있었다.

 

위의 코드에서 update_one()은 asyncio.run()을 통해 실행이되면 새로운 loop가 나와서 실행되고, AsyncIOMotor()를 통해 데이터베이스에 접근할 때에는 AsyncIOMotor()를 선언할 때 생긴 loop를 이용하기 때문에 이 둘의 loop를 혼용해서 에러가 발생하는 것이다.

 

그렇지만 여기서 의문인 점이 get_event_loop()를 통해서 나온 loop는 AsyncIOMotor()와 같다는 것인가? 라는 것이다.

 

그리고 다음 참고자료를 통해 확실시 했다.

https://gist.github.com/kaelzhang/e2ebdd03b61a28d879bfb74427fc4f46#file-get_event_loop-vs-get_running_loop-py

 

Show a demo the differences between Python `asyncio.get_event_loop()` and (vs) `asyncio.get_running_loop()`. And also explain th

Show a demo the differences between Python `asyncio.get_event_loop()` and (vs) `asyncio.get_running_loop()`. And also explain the differences between `asyncio.run()` and `loop.run_until_complete()`...

gist.github.com

위의 자료를 간단히 설명하자면,

get_event_loop를 통해 새롭게 생긴 loop들은 전부 같다. asyncio.run()을 통해 실행시키면 새로운 loop가 생성되고, 끝나면 현재 loop는 None이 된다. 즉, set_event_loop(None)으로 설정한다는 뜻이다. 그렇다면 asyncio.run()을 실행한 뒤에 asyncio.get_event_loop()하면 당연히 에러가 뜬다.

참고로 get_event_loop는 loop를 새롭게 생성하고 계속 선언해도 같은 current loop를 가리키지만, get_running_loop()는 현재 진행되고 있는 loop를 알려준다.

async def main():
    loop3 = asyncio.get_running_loop()
    print(loop3 is loop1)
    await test(loop3)

asyncio.run(main())

위의 코드는 에러가 안생기지만, get_running_loop()가 asyncio.run 이후나 이전에 선언되면 당연히 에러가 생긴다.

 

loop1 = asyncio.get_event_loop()

loop2 = asyncio.get_event_loop()

# loop1 == loop2

확인해보고싶어서 위 코드를 실행해봤고, 결론적으로 get_event_loop()를 통해 생성된 loop는 다음에 생성해서 현재 loop를 가져온다는 점이고 당연히 같은 loop인 것이다.

 

그렇다면 AsyncIOMotor가 loop를 생성할 때 get_event_loop()를 통해 생성되면 된다.

 

https://github.com/mongodb/motor/blob/master/motor/core.py

 

GitHub - mongodb/motor: Motor - the async Python driver for MongoDB and Tornado or asyncio

Motor - the async Python driver for MongoDB and Tornado or asyncio - GitHub - mongodb/motor: Motor - the async Python driver for MongoDB and Tornado or asyncio

github.com

다음의 github을 봤을 때 get_event_loop를 통해 loop를 가져온다.

 

asyncio.run()은 되도록 안쓰는것을 추천하는 것 같다. 만약 쓰고싶다면 현재 다른 코루틴 loop가 쓰이고 있는지는 않는지, 다시 loop를 쓰고서 set_event_loop()를 설정해줘야하는지 등등 event loop를 관리를 해줘야한다고 한다.

'Python' 카테고리의 다른 글

python parameter에서 bare *란?  (0) 2022.06.20
python xxx is not a package  (0) 2022.03.21
os.system significantly take time more than shutil.  (0) 2021.12.15
(에러)Asyncssh error Host key is not trusted  (0) 2021.11.15
python zip folder  (0) 2021.11.15