ZeroIntensity / view.py

The Batteries-Detachable Web Framework
https://view.zintensity.dev
MIT License
205 stars 15 forks source link

ASGI server #19

Open ZeroIntensity opened 1 year ago

ZeroIntensity commented 1 year ago

Oh boy, this is gonna be a big one.

We need a fast, ASGI server that is written entirely in C.

ZeroIntensity commented 10 months ago

For anyone insane enough to try this before I do, here's some information on the C API:

Async Python stuff is handled entirely by the PyAwaitable API. For more information on what that is, take a look at this discussion.

The "awaitable" in the PyAwaitable API (created by PyAwaitable_New) is a sort of proxy object that holds all coroutines submitted to it and holds a proper generator for it. Each coroutine holds a callback (which may be NULL) to be called when the value is received. Then, PyAwaitable_AddAwait takes four parameters:

Some other notable PyAwaitable methods:

An example of PyAwaitable:

static int my_callback(PyObject* awaitable, PyObject* result) {
    PyObject* some_value;
    if (PyAwaitable_UnpackValues(awaitable, &some_value) < 0) { // unpack the some_value reference
        return -1;
    }

    // some_value is now a borrowed reference to the some_value object saved earlier
    PyObject_Print(result, stdout, Py_PRINT_RAW);
    puts("");

    return 0;
}

// pretend this is mapped to a my_func in Python
static PyObject* my_func(PyObject* self, PyObject* args) {
    PyObject* awaitable_func;
    PyObject* some_value;
    if (!PyArg_ParseTuple(args, "OO", &awaitable_func, &some_value)
        return NULL;
    // assume that awaitable_func is indeed an awaitable function

    PyObject* awaitable = PyAwaitable_New();
    if (!awaitable) return NULL;

    if (PyAwaitable_SaveValues(awaitable, 1, some_value) < 0) { // save a reference to some_value in awaitable
        Py_DECREF(awaitable):
        return NULL;
    }

    PyObject* coro = PyObject_CallNoArgs(awaitable_func);
    if (!coro) {
        Py_DECREF(awaitable);
        return NULL;
    }

    if (PyAwaitable_AddAwait(awaitable, coro, my_callback, NULL) < 0) { // submit coro to the awaitable
        // error occurred, decrement our references
        Py_DECREF(coro);
        Py_DECREF(awaitable);
        return NULL;
    }

    return awaitable; // the awaitable must get returned to make the function usable via `await`
}