Skip to content

Instantly share code, notes, and snippets.

@erikbern
Last active November 23, 2021 13:16
Show Gist options
  • Save erikbern/a7fdbb2241c479464fed6c0f24d3a5cf to your computer and use it in GitHub Desktop.
Save erikbern/a7fdbb2241c479464fed6c0f24d3a5cf to your computer and use it in GitHub Desktop.
import asyncio
class AsyncConstructorMeta(type):
"""Metaclass to support asynchronous constructors in Python.
Basically we're exploiting the fact that __new__ can return anything in Python.
So we're taking the old __init__ code, removing it from the class, and instead,
we create a custom __new__ method that returns a coroutine wrapping the original
constructor.
This is horrible code. Please don't use this to launch rockets in outer space.
"""
def __new__(metacls, name, bases, dct):
new_dct = dict(dct)
old_init = new_dct.pop("__init__")
new_cls = super().__new__(metacls, name, bases, new_dct)
def new(cls, *args, **kwargs):
async def init():
obj = object.__new__(new_cls)
await old_init(obj, *args, **kwargs)
return obj
return init()
new_cls.__new__ = new
return new_cls
class Example(metaclass=AsyncConstructorMeta):
async def __init__(self, x):
await asyncio.sleep(1.0)
self.x = x
async def run():
a = await Example(x=42)
print(a.x)
asyncio.run(run())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment