How can I write asyncio coroutines that optionally act as regular functions?

前端 未结 3 1285
情话喂你
情话喂你 2020-12-02 16:06

I\'m writing a library that I\'d like end-users to be able to optionally use as if its methods and functions were not coroutines.

For example, given this function:

3条回答
  •  我在风中等你
    2020-12-02 16:35

    I agree with Andrew's answer, I just want to add that if you're dealing with objects, rather than top-level functions, you can use a metaclass to add synchronous versions of your asynchronous methods automatically. See this example:

    import asyncio
    import aiohttp
    
    class SyncAdder(type):
        """ A metaclass which adds synchronous version of coroutines.
    
        This metaclass finds all coroutine functions defined on a class
        and adds a synchronous version with a '_s' suffix appended to the
        original function name.
    
        """
        def __new__(cls, clsname, bases, dct, **kwargs):
            new_dct = {}
            for name,val in dct.items():
                # Make a sync version of all coroutine functions
                if asyncio.iscoroutinefunction(val):
                    meth = cls.sync_maker(name)
                    syncname = '{}_s'.format(name)
                    meth.__name__ = syncname
                    meth.__qualname__ = '{}.{}'.format(clsname, syncname)
                    new_dct[syncname] = meth
            dct.update(new_dct)
            return super().__new__(cls, clsname, bases, dct)
    
        @staticmethod
        def sync_maker(func):
            def sync_func(self, *args, **kwargs):
                meth = getattr(self, func)
                return asyncio.get_event_loop().run_until_complete(meth(*args, **kwargs))
            return sync_func
    
    class Stuff(metaclass=SyncAdder):
        @asyncio.coroutine
        def getter(self, url):
            return (yield from aiohttp.request('GET', url))
    

    Usage:

    >>> import aio, asyncio
    >>> aio.Stuff.getter_s
    
    >>> aio.Stuff.getter
    
    >>> s = aio.Stuff()
    >>> s.getter_s('http://example.com')
    
    
    >>> asyncio.get_event_loop().run_until_complete(s.getter('http://example.com'))
    
    
    

提交回复
热议问题