def _one_at_a_time(method):
    """decorator to limit an async method to be called only once

    If multiple concurrent calls to this method are made,
    queue them instead of allowing them to be concurrently outstanding.
    """
    # use weak dict for locks
    # so that the lock is always acquired within the current asyncio loop
    # should only be relevant in testing, where eventloops are created and destroyed often
    method._locks = WeakKeyDictionary()

    @wraps(method)
    async def locked_method(*args, **kwargs):
        loop = asyncio.get_event_loop()
        lock = method._locks.get(loop, None)
        if lock is None:
            lock = method._locks[loop] = asyncio.Lock()
        async with lock:
            return await method(*args, **kwargs)

    return locked_method
decorator to limit an async method to be called only once

If multiple concurrent calls to this method are made,
queue them instead of allowing them to be concurrently outstanding.
jupyterhub/proxy.py