send_message() 从线程中调用。由于 my_channel.send() 是异步的,我认为有必要让同步 send_message() 调用异步 asend_message() 。怎么办?
#@typechecked
class MessageCog(Cog):
@typechecked
async def asend_message(self, message:str)->str:
my_channel:TextChannel = self.bot.get_channel(self.channel)
assert isinstance(my_channel, TextChannel)
embed :Embed = Embed(title="Response", description=message, color=pepe_green)
assert isinstance(embed, Embed)
my_message:Message = await my_channel.send(embed=embed)
assert isinstance(my_message, Message)
result :str = str(my_message) # ??
assert isinstance(result, str)
if not isinstance(result, str): raise Exception()
return result
@typechecked
def send_message(self, message:str)->str:
#
#f:Coroutine = lambda m: self.asend_message(m)
#result:str = run(f)
#
#result:str = run(self.asend_message(message))
#
#result:str = get_running_loop().run_untilcomplete(self.asend_message(message))
#
#f:Callable[[str],str] = async_to_sync(MessageCog.asend_message)
#
#f = async_to_sync(MessageCog.asend_message)
#result:str = f(self, message)
#
#result:str = create_task(self.asend_message(message))
#
#f = partial(MessageCog.asend_message, self)
#result:str = run(f(message))
assert isinstance(result, str)
if not isinstance(result, str): raise Exception()
return result
错误总是相同的:
[chain/error] [chain:AgentExecutor] [82.96s] Chain run errored with error:
...
TypeError: type of the return value must be str; got coroutine instead
更新:新错误!
我想我终于找到了正确的类型。 async 返回一个返回 str 的协程。
async def asend_message(self, message:str)->Coroutine[str]:
lambda 创建一个可调用函数,返回一个协程...
f:Callable[[str],Coroutine[str]] = lambda m: self.asend_message(m)
g:Coroutine[str] = f(message)
result:str = run(g)
看到新错误后,我的第一反应是责怪 langchain,但我认为堆栈跟踪表明我使用 asyncio 的方式存在问题:
[tool/error] [chain:AgentExecutor > tool:send_message] [3ms] Tool run errored with error:
RuntimeError('Timeout context manager should be used inside a task')Traceback (most recent call last):
File "/home/user/.local/lib/python3.10/site-packages/langchain_core/tools.py", line 409, in run
context.run(
File "/home/user/.local/lib/python3.10/site-packages/langchain_core/tools.py", line 750, in _run
else self.func(*args, **kwargs)
File "/home/user/enumerate_bot/./eris.py", line 380, in <lambda>
func =lambda message: self. send_message(message),
File "/usr/lib/python3/dist-packages/typeguard.py", line 494, in wrapper
retval = func(*args, **kwargs)
File "/home/user/enumerate_bot/./eris.py", line 335, in send_message
result:str = run(g)
File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
return future.result()
File "/home/user/enumerate_bot/./eris.py", line 306, in asend_message
my_message:Message = await my_channel.send(embed=embed)
File "/home/user/.local/lib/python3.10/site-packages/discord/abc.py", line 1618, in send
data = await state.http.send_message(channel.id, params=params)
File "/home/user/.local/lib/python3.10/site-packages/discord/http.py", line 638, in request
async with self.__session.request(method, url, **kwargs) as response:
File "/home/user/.local/lib/python3.10/site-packages/aiohttp/client.py", line 1197, in __aenter__
self._resp = await self._coro
File "/home/user/.local/lib/python3.10/site-packages/aiohttp/client.py", line 507, in _request
with timer:
File "/home/user/.local/lib/python3.10/site-packages/aiohttp/helpers.py", line 715, in __enter__
raise RuntimeError(
RuntimeError: Timeout context manager should be used inside a task
如果您需要从尚未直接或间接从异步函数调用的函数或方法中调用异步函数或方法(即调用
asyncio.get_running_loop()
会引发异常),那么您只需使用 asyncio.run
方法。以下是当您不知道如何调用非异步函数时的更一般情况。
在此演示中,我有一个函数
bar
想要调用异步函数 foo
。有几种情况:
bar
在模块级别调用(非异步调用)。bar
由非异步函数 foobar
调用,该函数在模块级别调用。bar
和 foobar
都是从异步函数调用的。当 bar
被 async
函数直接或间接调用时,它会创建一个由其异步调用者返回并等待的任务。import asyncio
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
def run_async_from_non_async(async_fn, *args, **kwargs):
try:
# This can cause a deprecated warning and in the future
# will raise an exception:
loop = asyncio.get_running_loop()
print("asyncio.get_running_loop() succeeded")
return asyncio.create_task(async_fn(*args, **kwargs))
except RuntimeError:
print('asyncio.get_running_loop() failed')
try:
# Depending on what version of Python you are running
# this can result in a Deprecated Warning or raise
# an exception (in a future release)
loop = asyncio.get_event_loop()
except RuntimeError:
print('No event loop')
return asyncio.run(async_fn(*args, **kwargs))
else:
if loop.is_running():
# An event loop is running.
# We must have been called directly or indirectly
# from an async function. We just return a task that can
# be awaited:
print('asyncio.get_event_loop() returned a running loop')
return asyncio.create_task(async_fn(*args, **kwargs))
else:
# An event loop was created but is no longer running.
# Use it to run the async function:
print('asyncio.get_event_loop() returned a non-running loop')
return loop.run_until_complete(async_fn(*args, **kwargs))
async def foo(x):
await asyncio.sleep(1)
return x ** 2
def bar(x):
result = run_async_from_non_async(foo, x)
if asyncio.isfuture(result):
return result; # return back to caller
print('result =', result)
# Call the non-async function from a non-async function, which
# may or may not have been called directly or indirectly from an async function:
def foobar(x):
result = bar(x)
if asyncio.isfuture(result):
return result; # return back to caller
# Example usage from an async function
async def main():
print('result =', await bar(10)) # Call bar from async function
print('result =', await foobar(11)) # Call foobar from non-async function
# Running the example usage
print('result =', bar(7)) # Call bar from non-async function
print('result =', bar(8)) # Call bar from non-async function
foobar(9) # Call foobar from non-async function
asyncio.run(main())
打印:
asyncio.get_running_loop() failed
asyncio.get_event_loop() returned a non-running loop
result = 49
result = None
asyncio.get_running_loop() failed
asyncio.get_event_loop() returned a non-running loop
result = 64
result = None
asyncio.get_running_loop() failed
asyncio.get_event_loop() returned a non-running loop
result = 81
asyncio.get_running_loop() succeeded
result = 100
asyncio.get_running_loop() succeeded
result = 121