Test if function or method is normal or asynchronous

后端 未结 4 1614
野的像风
野的像风 2020-12-14 14:09

How can I find out if a function or method is a normal function or an async function? I would like my code to automatically support normal or async callbacks and need a way

相关标签:
4条回答
  • 2020-12-14 14:52

    If you don't want to introduce another import with inspect, iscoroutine is also available inside asyncio.

    import asyncio
    
    def isAsync(someFunc):
        return asyncio.iscoroutinefunction(someFunc)
    
    0 讨论(0)
  • 2020-12-14 14:56

    Co-routines have the COROUTINE flag set, bit 7 in the code flags:

    >>> async def foo(): pass
    >>> foo.__code__.co_flags & (1 << 7)
    128   # not 0, so the flag is set.
    

    The value 128 is stored as a constant in the inspect module:

    >>> import inspect
    >>> inspect.CO_COROUTINE
    128
    >>> foo.__code__.co_flags & inspect.CO_COROUTINE
    128
    

    The inspect.iscoroutinefunction() function does just that; test if the object is a function or method (to ensure there is a __code__ attribute) and test for that flag. See the source code.

    Of course, using inspect.iscoroutinefunction() is the most readable and guaranteed to continue to work if ever the code flags were to change:

    >>> inspect.iscoroutinefunction(foo)
    True
    
    0 讨论(0)
  • 2020-12-14 15:11

    Use the inspect module of Python.

    inspect.iscoroutinefunction(object)

    Return true if the object is a coroutine function (a function defined with an async def syntax).

    This function is available since Python 3.5. The module is available for Python 2 with lesser functionalities and certainly without the one you are looking for: inspect

    Inspect module as the name suggests is useful to inspect a whole lot of thing. The documentation says

    The inspect module provides several useful functions to help get information about live objects such as modules, classes, methods, functions, tracebacks, frame objects, and code objects. For example, it can help you examine the contents of a class, retrieve the source code of a method, extract and format the argument list for a function, or get all the information you need to display a detailed traceback.

    There are four main kinds of services provided by this module: type checking, getting source code, inspecting classes and functions, and examining the interpreter stack.

    Some basic capabilities of this module are:

    inspect.ismodule(object)
    inspect.isclass(object)
    inspect.ismethod(object)
    inspect.isfunction(object)
    

    It also packs capability to retrieve the source code

    inspect.getdoc(object)
    inspect.getcomments(object)
    inspect.getfile(object) 
    inspect.getmodule(object)
    

    Methods are named intuitively. Description if needed can be found in documentation.

    0 讨论(0)
  • 2020-12-14 15:11

    TLDR

    If you want to check something should be used with await, use inspect.isawaitable (as when you test something being callable() other than just being a function).

    Unlike iscoroutine or iscoroutinefunction it will also work for Futures and objects that implement __await__ method.


    Detailed

    Solutions above will work for simple cases, when you pass coroutine function. In some cases you may like to pass awaitable object function that acts like coroutine function, but is not coroutine function. Two examples is Future class or Future-like object class (class that implements __await__ magic method). In this cases iscoroutinefunction will return False, what is not you need.

    It's easier to understand on non-async example with passing non-function callable as callback:

    class SmartCallback:
        def __init__(self):
            print('SmartCallback is not function, but can be used as function')
    
    callCallback(SmartCallback)  # Should work, right?
    

    Back to async world, a similar situation:

    class AsyncSmartCallback:
        def __await__(self):
            return self._coro().__await__()
    
        async def _coro(self):
            print('AsyncSmartCallback is not coroutine function, but can be used as coroutine function')
            await asyncio.sleep(1)
    
    await callCallback(AsyncSmartCallback)  # Should work, but oops! iscoroutinefunction(AsyncSmartCallback) == False
    

    Way to solve it not to use iscoroutine or iscoroutinefunction, but use inspect.isawaitable instead. It works with ready object so you must create it first. In other words, solution I would advise to use:

    async def callCallback(cb, arg):
        if callable(cb):
            res = cb()  # here's result of regular func or awaitable
            if inspect.isawaitable(res):
                res = await res  # await if awaitable
            return res  # return final result
        else:
            raise ValueError('cb is not callable')
    

    It's more universal (and I'm sure logically correct) solution.

    0 讨论(0)
提交回复
热议问题