Günther Jena Günther Jena - 2 months ago 29
Python Question

Setting a descriptor in python3.5 asynchronously

I can write a descriptor returning a future which could be awaited on.

class AsyncDescriptor:
def __get__(self, obj, cls=None):
# generate some async future here
return future

def __set__(self, obj, value):
# generate some async future here
return future

class Device:
attr=AsyncDescriptor()

device=Device()


Now I can get the value in a coroutine with
value=await device.attr
.

How would I set this attribute?


  • await device.attr=5
    -> SyntaxError: can't assign to await expression

  • await setattr(device, 'attr', 5)
    -> TypeError: object NoneType can't be used in 'await' expression

  • device.attr=5
    -> RuntimeWarning: coroutine '__set__' was never awaited


Answer

What you are trying to do is not possible (with Python 3.5).

While it may be sensible for __get__ to return a Future, making __set__ async is simply not supported by Python 3.5. The return value of __set__ is ignored by Python since there is no "return value" of assignments. And the call to __set__ is always synchronous. Something like a = (b.c = 5) actually raises a SyntaxError as you already noticed.

If async assignments like await device.attr = 5 were allowed, there would probably be a separate protocol for async descriptors, i.e. with coroutines __aget__ and __aset__ as special methods in analogy to async context manager (async with / __aenter__ ) and async iteration (async for / __aiter__). See PEP 492 for the design decisions behind the async / await support.

Also note that __get__ returning a future does not make __get__ a coroutine.

Without further context, it looks like you want to hide something behind the attribute access abstraction provided by the descriptor protocol which should better be done explicitly, but that's up to you of course.