I don’t support this change at all - much for the reasons already posted by others.
I am writing , though, to remind that in many cases there are suitable workarounds to many of the things you want to achieve as motivation: enabling async code in special “dunder” methods such as `_setattr_` (I can imagine a pass-through setting to a network-baked object being convenient), or mainly, to avoid writing duplicate intermediate code so that one call-path composed of pure “async def” functions.
(As for properties, they work with async-def functions out of the box)
One workaround is when calling sync code from an async context, that would downstream have to await things (become async again), to take note of the current running loop, call the synchronous code in a thread, and then, when calling async again, schedule the async code in the original loop (and synchronously wait for it in the sync-thread).
That is the approach I take in my “extraasync” packages and its `sync_to_async` and `async_to_sync` function pairs, and you would most welcome to use and or collaborate with it.
Example of code using an async `_setattr_`:
import asyncio
from extraasync import async_to_sync, sync_to_async
class A:
def __setattr__(self, attr, value):
async def set():
await asyncio.sleep(1)
super(A, self).__setattr__(attr, value)
sync_to_async(set)
def trampoline(i):
a = A()
print(f"starting set to {i}")
a.b = i
print(a.b)
async def main():
t1 = async_to_sync(trampoline, (1,))
t2 = async_to_sync(trampoline, (2,))
await asyncio.sleep(0.5)
print("middle way")
await asyncio.gather(t1, t2)
asyncio.run(main())
The only thing on the writer perspective is that when calling sync code from async, if one ever intend to do async inside that call, it has to be done through the `async_to_sync` bridge.
If the code calling `sync_to_async` doesn’t perceive that it is in a context started in this way, it will spawn an event loop for the current thread instead (which will be reused for further sync_to_async calls)