tools (utils): Add maybe_async function:

`maybe_async` can be used to call a function which may be or not `async`. It doesn't use
`defer.ensureDeferred` contrary to `ad_defered`, so the call stack is not lost, and it's
easier to debug when an Exception is raised.
This commit is contained in:
Goffi 2025-10-01 17:57:47 +02:00
commit 286e931a7f

View file

@ -18,7 +18,7 @@
"""various useful methods"""
from typing import Optional, Union
from typing import Any, Optional, Union
import unicodedata
import os.path
import datetime
@ -92,12 +92,13 @@ def partial(func, *fixed_args, **fixed_kwargs):
return method
def as_deferred(func, *args, **kwargs):
def as_deferred(func, *args, **kwargs) -> defer.Deferred:
"""Call a method and return a Deferred
the method can be a simple callable, a Deferred or a coroutine.
It is similar to defer.maybeDeferred, but also handles coroutines
It is similar to defer.maybeDeferred, but also handles coroutines.
Due to the use of ``defer.ensureDeferred``, the call stack may be lost, which can make
debugging harder in case of Exception, the function ``maybe_async`` below solves this.
"""
try:
ret = func(*args, **kwargs)
@ -114,6 +115,26 @@ def as_deferred(func, *args, **kwargs):
return defer.succeed(ret)
async def maybe_async(ret: Any) -> Any:
"""Check if a return value is awaitable and await it if necessary.
This function checks whether the return value of a method is awaitable. If it is, the
function awaits on it and returns the awaited value. Otherwise, it returns the value
as-is.
Unlike ``as_deferred``, this function does not call ``defer.ensureDeferred``, so it
must be used within an already ``async`` context where ``defer.ensureDeferred`` has
already been called at a higher level. This makes debugging easier by preserving the
call stack in case of exceptions.
@param ret: The return value of a function, which may be awaitable or not.
@return: The awaited value if the input was awaitable, otherwise the input as-is.
"""
if inspect.isawaitable(ret):
return await ret
return ret
def aio(func):
"""Decorator to return a Deferred from asyncio coroutine