주피터 노트북을 사용할 때 "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))
하지만 다시 돌아온다
뭐가 문제야?
어떻게 풀까요?
문서에는 다음과 같이 나와 있습니다:
이 함수는 같은 스레드에서 다른 비동기 이벤트 루프가 실행될 때 호출됩니다.
당신의 경우 주피터()는 이미 이벤트 루프를 실행하고 있다:
이제 IPython 터미널과 노트북에서 상위 레벨에서 비동기/대기를 사용할 수 있습니다. 대부분의 경우 "그냥 작동"해야 합니다. IPython을 버전 7+로 업데이트하고, IPykernel을 버전 5+로 업데이트하면 경주에 나갈 수 있습니다.
따라서 이벤트 루프를 직접 시작할 필요가 없으며 코드가 비동기 함수 외부에 있더라도 직접 호출할 수 있습니다.
Jupyter (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
주의
아이피톤에 비해 주피터가 루프를 어떻게 사용하는지에 대한 이유가 있다.
구글 콜랩
과거에는 구글 collab에서 더 복잡한 루프 조작을 요구했지만, 이제는 iPython ≥ 7.0(버전에서 테스트됨)에서와 같이 평이하게 작동해야 한다.
의 답변에 추가하기 위해 - 만약 누군가가 루프가 실행되고 있는지를 감지하고 자동으로 조정하기를 원한다면, 다음과 같이 유용함을 증명할 수 있는 토막글이 있다:
# 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())
그 문서에 언급된 내용에 의하면
이 함수는 같은 스레드에서 다른 비동기 이벤트 루프가 실행 중일 때 호출할 수 없습니다.
다른 스레드를 사용할 수 있습니다. 즉 -
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]
판카즈 샤르마와 장 모네의 방법들을 결합하여, 나는 다음과 같은 syncio.run의 역할을 하지만 (조금 다른 구문을 가진) 주피터 노트 내에서도 작동하는 토막을 썼다.
import threading
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"
그냥 이것만 사용하면 됩니다:
https://github.com/erdewit/nest_asyncio
import nest_asyncio
nest_asyncio.apply()
Python 스크립트와 Jupyter 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())
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)
예를 들어, 예를 들어, ...와 같이 사용합니다.
기존의 답변에 추가하기 위해, 외부 라이브러리가 없는 다소 짧은 버전으로, 목성 내부와 외부를 실행할 수 있으며 반환 값을 가져올 수 있다:
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())
'개발하자' 카테고리의 다른 글
주피터 노트북 사용 권한을 로드하는 동안 오류가 발생했습니다. iynb (0) | 2023.09.27 |
---|---|
플러터의 이미지 맵 같은 기능? (0) | 2023.09.27 |
파이썬에서 코드 객체를 만드는 방법은? (0) | 2023.09.26 |
how to select a long list of id's in sql using python (0) | 2023.09.25 |
여러 줄 문자열 내에서 파이썬 대체 (0) | 2023.09.25 |