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:>
Also you can create a simple decorator which makes your function synchronious. This approach can be applied to global functions and to methods.
An example.
# the decorator
def sync(f):
ASYNC_KEY = 'async'
def f_in(*args, **kwargs):
if ASYNC_KEY in kwargs:
async = kwargs.get(ASYNC_KEY)
del kwargs[ASYNC_KEY]
else:
async = True
if async:
return f(*args, **kwargs)
else:
return asyncio.get_event_loop().run_until_complete(f())
return f_in
# below: the usage
@sync
async def test():
print('In sleep...')
await asyncio.sleep(1)
print('After sleep')
# below: or asyncio.get_event_loop().create_task(test())
asyncio.get_event_loop().run_until_complete(test())
# and here is your syncronious version
test(async=False)
Moreover: it probably have sense to create special wrapper class not to pass async to every method call. The example is below.
class SyncCallerWrapper(object):
def __init__(self, obj, is_async=True):
self._obj = obj
self._is_async = is_async
def __getattr__(self, name):
def sync_wrapper(obj_attr):
def f(*args, **kwargs):
return asyncio.get_event_loop().run_until_complete(obj_attr(*args, **kwargs))
return f
obj_attr = getattr(self._obj, name)
if not self._is_async and asyncio.iscoroutinefunction(obj_attr):
return sync_wrapper(obj_attr)
return obj_attr
class C(object):
async def sleep1(self):
print('In sleep1...')
await asyncio.sleep(1)
print('After sleep1')
async def sleep2(self):
print('In sleep2...')
await asyncio.sleep(1)
print('After sleep2')
# you don't want any concurrency in your code
c_sync = SyncCallerWrapper(C(), is_async=False)
c_sync.sleep1()
c_sync.sleep2()
# here you want concurrency: class methods are coroutines
c_async = SyncCallerWrapper(C(), is_async=True)
asyncio.get_event_loop().run_until_complete(c_async.sleep1())
asyncio.get_event_loop().run_until_complete(c_async.sleep2())
To be more elegant you can replace your class with a function (global constructor). Then a user could create class C passing is_async parameter and have desired behaviour: methods will act as regular (is_async=False) or as async functions (is_async=True).
def C(*args, **kwargs):
KEY_ISASYNC = 'is_async'
if KEY_ISASYNC in kwargs:
is_async = kwargs.get(KEY_ISASYNC)
del kwargs[KEY_ISASYNC]
else:
is_async = False
return SyncCallerWrapper(_C(*args, **kwargs), is_async=is_async)
# you don't want any concurrency in your code
c_sync = C(is_async=False)
c_sync.sleep1()
c_sync.sleep2()
# here you want concurrency: class methods are coroutines
c_async = C(is_async=True)
asyncio.get_event_loop().run_until_complete(c_async.sleep1())
asyncio.get_event_loop().run_until_complete(c_async.sleep2())