给定一个具有同步方法但在另一个类中调用异步方法的类。当我尝试在同步类中调用同步方法时,我仍然得到协程从未等待过。以下是示例脚本
from time import perf_counter as timer
from functools import wraps
from typing import Union
from inspect import iscoroutinefunction
from dscommonutil.logger.basic import Logger
log = Logger("abc", ".")
log.start()
def timeit(task: str, logger: Union[Logger, str] = "log"):
def _timeit(func):
nonlocal logger
if isinstance(logger, Logger):
obj_check = False
elif isinstance(logger, str):
obj_check = True
else:
raise ValueError(
f"{logger} needs to be a valid logging object or"
" an attribute of args[0] parameter."
)
@wraps(func)
async def wrapper(*args, **kwargs):
nonlocal logger
start_time = timer()
if obj_check and (
isinstance(args[0], object) and getattr(args[0], logger, False)
):
logger = getattr(args[0], logger)
if iscoroutinefunction(func):
value = await func(*args, **kwargs)
else:
value = func(*args, **kwargs)
logger.info_metric(
f"Time elapsed in {task}",
round(timer() - start_time, 2)
)
return value
return wrapper
return _timeit
class Async:
def __init__(self):
self.log = log
@timeit(task="running sync function")
def sync(self, arg):
self.log.info_print("Inside Synchronous Function")
return arg
@timeit(task="running async function")
async def caller(self, arg):
self.log.info_print("Inside Asynchronous Function")
value = await self.sync(arg)
return value
class Client:
def __init__(self):
self.log = log
@timeit(task="random func")
def func(self):
self.log.info_print("Inside func from client")
return "Hello"
def main(self):
print(self.func())
a = Async()
return a.caller("abc")
if __name__ == '__main__':
import asyncio
c = Client()
asyncio.run(c.main())
注意: dscommonutil 是一些内部开发的日志记录库,这是纯同步的。
问题主要是在拨打
self.func()
时出现,显示为RuntimeWarning: coroutine 'Client.func' was never awaited
。
任何人都可以帮助理解这里调用函数的适当方法是什么,以及如何在不使用
await
的情况下进行同步调用?
如果我简单地添加代码就可以工作
value = await self.func()
print(value)
我想更多地了解 Python 的异步功能,据我所知,这不应该被
inspect.iscoroutinefunction
检测为协程,但不知何故它确实这样做了。
我尝试运行你的代码,问题似乎出在
async def wrapper(*args, **kwargs)
。由于它是一个异步装饰器,因此它希望您等待它,即使它包装了同步方法。您可能会考虑使包装器同步。这个例子可能对你有帮助。
def timeit(func):
def _timeit(func):
nonlocal logger
if isinstance(logger, Logger):
obj_check = False
elif isinstance(logger, str):
obj_check = True
else:
raise ValueError(
f"{logger} needs to be a valid logging object or"
" an attribute of args[0] parameter."
)
@functools.wraps(func)
def wrapper(*args, **kwargs):
nonlocal logger
start_time = timer()
_timeit(func)
if obj_check and (
isinstance(args[0], object) and getattr(args[0], logger, False)
):
logger = getattr(args[0], logger)
if not asyncio.iscoroutinefunction(func):
return func(*args, **kwargs)
else:
async def tmp():
return (await func(*args, **kwargs))
return tmp()
return wrapper