@@ -86,6 +86,73 @@ hidden; you give PyAwaitable your coroutine, and it handles the rest!
8686
8787 Yay! We called an asynchronous function from C!
8888
89+ Simpler ``PyAwaitable_AddAwait `` Calls
90+ --------------------------------------
91+
92+ But, what if we wanted to call the ``async def `` function from the C API?
93+ With our current knowledge, that would look like this:
94+
95+ .. code-block :: c
96+
97+ static PyObject *
98+ trampoline(PyObject *self, PyObject *func) // METH_O
99+ {
100+ PyObject *awaitable = PyAwaitable_New();
101+ if (awaitable == NULL) {
102+ return NULL;
103+ }
104+
105+ PyObject *coro = PyObject_CallNoArgs(func);
106+ if (coro == NULL) {
107+ Py_DECREF(awaitable);
108+ return NULL;
109+ }
110+
111+ if (PyAwaitable_AddAwait(awaitable, coro, NULL, NULL) < 0) {
112+ Py_DECREF(awaitable);
113+ Py_DECREF(coro);
114+ return NULL;
115+ }
116+
117+ Py_DECREF(coro);
118+ return awaitable;
119+ }
120+
121+ Ouch, that's a lot of boilerplate. Luckily, PyAwaitable provides a convenience
122+ function for this case: :c:func: `PyAwaitable_AddExpr `. This function is very
123+ similar to :c:func: `PyAwaitable_AddAwait `, but it has two additional semantics
124+ for the passed coroutine:
125+
126+ - If the coroutine is ``NULL ``, it returns ``-1 `` without setting an
127+ exception.
128+ - If the coroutine is non-``NULL ``, it passes it to
129+ :c:func: `PyAwaitable_AddAwait ` and then decrements its reference count
130+ ("stealing a reference").
131+
132+ These properties make it possible to directly use the result of a C API
133+ function without extra boilerplate, because errors will be propagated when
134+ it fails (when the coroutine is ``NULL ``) and the reference count will be
135+ decremented, preventing leaks.
136+
137+ So, with that in mind, we can rewrite our example as the following:
138+
139+ .. code-block :: c
140+
141+ static PyObject *
142+ trampoline(PyObject *self, PyObject *func) // METH_O
143+ {
144+ PyObject *awaitable = PyAwaitable_New();
145+ if (awaitable == NULL) {
146+ return NULL;
147+ }
148+
149+ if (PyAwaitable_AddExpr(awaitable, PyObject_CallNoArgs(func), NULL, NULL) < 0) {
150+ Py_DECREF(awaitable);
151+ return NULL;
152+ }
153+
154+ return awaitable;
155+ }
89156
90157 .. _return-value-callbacks :
91158
@@ -138,14 +205,7 @@ Now, we can use the result of ``silly()`` in C:
138205 return NULL;
139206 }
140207
141- // Get the coroutine by calling silly()
142- PyObject *coro = PyObject_CallNoArgs(silly);
143- if (coro == NULL) {
144- Py_DECREF(awaitable);
145- return NULL;
146- }
147-
148- if (PyAwaitable_AddAwait(awaitable, coro, callback, NULL) < 0) {
208+ if (PyAwaitable_AddExpr(awaitable, PyObject_CallNoArgs(silly), callback, NULL) < 0) {
149209 Py_DECREF(awaitable);
150210 Py_DECREF(coro);
151211 return NULL;
@@ -279,24 +339,11 @@ In C, all that would be implemented like this:
279339 return NULL;
280340 }
281341
282- // Remember, this isn't the same as executing the coroutine, so
283- // the timeout doesn't show up here. But, we still need to handle
284- // an exception case, because something might have gone wrong
285- // in getting the coroutine object, e.g., the object isn't callable
286- // or we're out of memory.
287- PyObject *coro = PyObject_CallNoArgs(make_request);
288- if (coro == NULL) {
342+ if (PyAwaitable_AddExpr(awaitable, PyObject_CallNoArgs(coro), return_true, return_false)) {
289343 Py_DECREF(awaitable);
290344 return NULL;
291345 }
292346
293- if (PyAwaitable_AddAwait(awaitable, coro, return_true, return_false)) {
294- Py_DECREF(awaitable);
295- Py_DECREF(coro);
296- return NULL;
297- }
298-
299- Py_DECREF(coro);
300347 return awaitable;
301348 }
302349
0 commit comments