주피터 노트북을 사용할 때 "실행 중인 이벤트 루프에서 asyncio.run()을 호출할 수 없음"
비동기식으로 웹페이지 html을 받고 싶습니다.
주피터 노트북에서 다음 코드를 실행합니다.
import aiofiles
import aiohttp
from aiohttp import ClientSession
async def get_info(url, session):
resp = await session.request(method="GET", url=url)
resp.raise_for_status()
html = await resp.text(encoding='GB18030')
with open('test_asyncio.html', 'w', encoding='utf-8-sig') as f:
f.write(html)
return html
async def main(urls):
async with ClientSession() as session:
tasks = [get_info(url, session) for url in urls]
return await asyncio.gather(*tasks)
if __name__ == "__main__":
url = ['http://huanyuntianxiazh.fang.com/house/1010123799/housedetail.htm', 'http://zhaoshangyonghefu010.fang.com/house/1010126863/housedetail.htm']
result = asyncio.run(main(url))
그러나, 그것은 돌아옵니다.RuntimeError: asyncio.run() cannot be called from a running event loop
뭐가 문제야?
어떻게 풀까요?
설명서에는 다음과 같이 나와 있습니다.
다른 비동기 이벤트 루프가 동일한 스레드에서 실행 중일 때는 이 함수를 호출할 수 없습니다.
이 경우 주피터(IPython ≥ 7.0)는 이미 이벤트 루프를 실행하고 있습니다.
이제 IPython 터미널에서 최상위 수준의 비동기/대기를 사용할 수 있으며, 노트북에서는 대부분의 경우 "그냥 작동"해야 합니다.IPython을 버전 7+로 업데이트하고 커널을 버전 5+로 업데이트하면 레이스를 시작할 수 있습니다.
따라서 이벤트 루프를 직접 시작할 필요가 없으며 대신 전화를 걸 수 있습니다.await main(url)
직접적으로, 코드가 비동기 함수의 외부에 있더라도.
목성 (IPython ≥ 7.0)
async def main():
print(1)
await main()
Python » 3.7 및 IPython < 7.0
import asyncio
async def main():
print(1)
asyncio.run(main())
코드에서 다음을 제공합니다.
url = ['url1', 'url2']
result = await main(url)
for text in result:
pass # text contains your html (text) response
주의.
주피터가 루프를 사용하는 방식은 IPython과 비교했을 때 약간의 차이가 있습니다.
추가하기cglacet
의 답변 - 루프가 실행 중인지 여부를 감지하고 자동으로 조정(즉, 실행)하려는 경우main()
기존 루프에서, 그렇지 않으면asyncio.run()
), 다음은 유용할 수 있는 코드 조각입니다.
# async def main():
# ...
try:
loop = asyncio.get_running_loop()
except RuntimeError: # 'RuntimeError: There is no current event loop...'
loop = None
if loop and loop.is_running():
print('Async event loop already running. Adding coroutine to the event loop.')
tsk = loop.create_task(main())
# ^-- https://docs.python.org/3/library/asyncio-task.html#task-object
# Optionally, a callback function can be executed when the coroutine completes
tsk.add_done_callback(
lambda t: print(f'Task done with result={t.result()} << return val of main()'))
else:
print('Starting new event loop')
result = asyncio.run(main())
다음을 사용합니다.
https://github.com/erdewit/nest_asyncio
import nest_asyncio
nest_asyncio.apply()
Pankaj Sharma와 Jean Monet의 방법을 결합하여 다음 스니펫을 작성했습니다. asyncio.run 역할을 하지만 주피터 노트북에서도 작동합니다.
class RunThread(threading.Thread):
def __init__(self, func, args, kwargs):
self.func = func
self.args = args
self.kwargs = kwargs
self.result = None
super().__init__()
def run(self):
self.result = asyncio.run(self.func(*self.args, **self.kwargs))
def run_async(func, *args, **kwargs):
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = None
if loop and loop.is_running():
thread = RunThread(func, args, kwargs)
thread.start()
thread.join()
return thread.result
else:
return asyncio.run(func(*args, **kwargs))
용도:
async def test(name):
await asyncio.sleep(5)
return f"hello {name}"
run_async(test, "user") # blocks for 5 seconds and returns "hello user"
저는 패키지가 파이썬 스크립트와 주피터 REPL에서 동일한 방식으로 동작하는 코드를 작성하는 데 유용하다는 것을 알게 되었습니다.
import asyncio
from unsync import unsync
@unsync
async def demo_async_fn():
await asyncio.sleep(0.1)
return "done!"
print(demo_async_fn().result())
기존 답변에 추가하자면, 외부 라이브러리가 없는 다소 짧은 버전으로 주피터 내부와 외부에서 실행되고 반환 값을 가져올 수 있습니다.
try:
asyncio.get_running_loop()
# we need to create a separate thread so we can block before returning
with ThreadPoolExecutor(1) as pool:
result = pool.submit(lambda: asyncio.run(myfunc()))).result()
except RuntimeError:
# no event loop running
result = asyncio.run(myfunc())
Cglace가 언급한 바와 같이 설명서가 나와 있습니다.
다른 비동기 이벤트 루프가 동일한 스레드에서 실행 중일 때는 이 함수를 호출할 수 없습니다.
다른 스레드를 사용할 수 있습니다. 예:
class ResolveThread(threading.Thread):
def __init__(self,result1,fun,url):
self.result1= result1
self.fun = fun
self.url = url
threading.Thread.__init__(self)
def run(self):
result1[0] = asyncio.run(self.fun(self.url))
result1 = [None]
sp = ResolveThread(result1)
sp.start()
sp.join() # connect main thread
result = result1[0]
Mark에 의한 솔루션의 약간 단순화:
import threading
class RunThread(threading.Thread):
def __init__(self, coro):
self.coro = coro
self.result = None
super().__init__()
def run(self):
self.result = asyncio.run(self.coro)
def run_async(coro):
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = None
if loop and loop.is_running():
thread = RunThread(coro)
thread.start()
thread.join()
return thread.result
else:
return asyncio.run(coro)
사용하다run_async()
맘에 들다async.run()
,예.,run_async(test("user"))
.
언급URL : https://stackoverflow.com/questions/55409641/asyncio-run-cannot-be-called-from-a-running-event-loop-when-using-jupyter-no
'programing' 카테고리의 다른 글
트위터 부트스트랩을 사용하여 모달/대화 상자에서 삭제를 확인하시겠습니까? (0) | 2023.05.14 |
---|---|
플라스크 사용자 인증 (0) | 2023.05.14 |
일반 PropertyMetadata보다 FrameworkPropertyMetadata 또는 UIPPropertyMetadata를 언제 사용해야 합니까? (0) | 2023.05.09 |
셀에서 마지막 하위 문자열 추출 (0) | 2023.05.09 |
Azure 서비스 패브릭 대 Azure 컨테이너 서비스 (0) | 2023.05.09 |