Skip to content

Commit cfa62d6

Browse files
committed
Changes.
Signed-off-by: AraHaan <[email protected]>
1 parent f1f57e9 commit cfa62d6

File tree

3 files changed

+48
-9
lines changed

3 files changed

+48
-9
lines changed

docs/reference.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,22 @@ Coroutines
114114
.. versionadded:: 2.1
115115
116116
117+
.. c:function:: int PyAwaitable_AsyncWith(PyObject *awaitable, PyObject *context, PyAwaitable_Callback result_callback, PyAwaitable_Error error_callback)
118+
119+
Similar to :c:func:`PyAwaitable_AddAwait` and :c:func:`PyAwaitable_AddExpr`
120+
except the target class implements the Async Context Manager.
121+
122+
if *awaitable* or *context* is ``NULL``, this function returns ``-1`` and sets
123+
an exception. if *awaitable* and *context* are non-``NULL``, this function
124+
then calls its :meth:`~object.__aenter__` and :meth:`~object.__aexit__` members
125+
for the user.
126+
127+
.. note::
128+
129+
If *context* does not implement one of :meth:`~object.__aenter__` or
130+
:meth:`~object.__aexit__` the result is ``-1`` and sets an exception
131+
as it is then not considered to be an Async Context Manager.
132+
117133
Value Storage
118134
-------------
119135

docs/usage/adding_awaits.rst

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ Using ``async with`` from C
160160
---------------------------
161161
Unlike the :c:func:`PyAwaitable_AddAwait` and the :c:func:`PyAwaitable_AddExpr`
162162
functions there exists :c:func:`PyAwaitable_AsyncWith` that will call the
163-
`__aenter__` and `__aexit__` members of a class if it implements the Async Context
164-
Manager.
163+
:meth:`~object.__aenter__` and :meth:`~object.__aexit__` members of a class
164+
if it implements the Async Context Manager.
165165

166166
Using this on a type that is designed with `async with` in mind that automatically
167167
cleans up resources when the scope leaves the context manager can be much easier
@@ -177,12 +177,31 @@ than manually making those calls to clean them up.
177177
.. note::
178178

179179
This example uses the `asqlite` library created by Rapptz to allow using the
180-
`sqlite3` module from within Asynchronous code safely. It also uses a helper header
181-
file called `awaitfunc.h` from the https://github.com/AraHaan/awaitfunc github
182-
repository.
180+
`sqlite3` module from within Asynchronous code safely.
183181

184182
.. code-block:: c
185183
184+
static PyObject *
185+
_PyObject_GetCallableMethod(PyObject *obj, PyObject *attr_name) {
186+
if (attr_name == NULL) {
187+
// the python string is null.
188+
return NULL;
189+
}
190+
191+
PyObject *method = PyObject_GetAttr(obj, attr_name);
192+
if (!PyCallable_Check(method)) {
193+
Py_DECREF(attr_name);
194+
Py_XDECREF(method);
195+
return NULL;
196+
}
197+
198+
Py_DECREF(attr_name);
199+
return method;
200+
}
201+
202+
#define PyObject_GetCallableMethod(obj, name) _PyObject_GetCallableMethod(_PyObject_CAST(obj), name)
203+
#define PyObject_GetCallableMethodString(obj, name) PyObject_GetCallableMethod(obj, PyUnicode_FromString(name))
204+
186205
static int
187206
add_or_delete_items_cursor_cb(PyObject *awaitable, PyObject *cursor) {
188207
if (cursor != NULL && !Py_IsNone(cursor)) {
@@ -192,11 +211,11 @@ than manually making those calls to clean them up.
192211
return -1;
193212
}
194213
195-
if (PyAwaitable_AwaitFunction(awaitable, PyObject_GetCallableMethodString(cursor, "executemany"), "OO", NULL, NULL, PySequence_GetItem(args, 0), PySequence_GetItem(args, 1)) < 0) {
214+
if (PyAwaitable_AddExpr(awaitable, PyObject_CallFunction(PyObject_GetCallableMethodString(cursor, "executemany"), "OO", PySequence_GetItem(args, 0), PySequence_GetItem(args, 1)), NULL, NULL) < 0) {
196215
return -1;
197216
}
198217
199-
if (PyAwaitable_AwaitFunctionNoArgs(awaitable, PyObject_GetCallableMethodString(connection, "commit"), NULL, NULL) < 0) {
218+
if (PyAwaitable_AddExpr(awaitable, PyObject_CallNoArgs(PyObject_GetCallableMethodString(connection, "commit")), NULL, NULL) < 0) {
200219
return -1;
201220
}
202221
@@ -220,7 +239,7 @@ than manually making those calls to clean them up.
220239
return -1;
221240
}
222241
223-
if (PyAwaitable_AsyncWithFunctionNoArgs(awaitable, PyObject_GetCallableMethodString(connection, "cursor"), add_or_delete_items_cursor_cb, NULL) < 0) {
242+
if (PyAwaitable_AsyncWith(awaitable, PyObject_CallNoArgs(PyObject_GetCallableMethodString(connection, "cursor")), add_or_delete_items_cursor_cb, NULL) < 0) {
224243
return -1;
225244
}
226245
@@ -249,7 +268,7 @@ than manually making those calls to clean them up.
249268
}
250269
251270
DiscordBot_State *state = get_DiscordBot_state(mod);
252-
if (PyAwaitable_AsyncWithFunction(awaitable, PyObject_GetCallableMethodString(state->asqliteModule, "connect"), "N", add_or_delete_items_connect_cb, NULL, dbString) < 0) {
271+
if (PyAwaitable_AsyncWith(awaitable, PyObject_CallFunction(PyObject_GetCallableMethodString(state->asqliteModule, "connect"), "N", dbString), add_or_delete_items_connect_cb, NULL) < 0) {
253272
Py_XDECREF(awaitable);
254273
return NULL;
255274
}

src/_pyawaitable/with.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ PyAwaitable_AsyncWith(
9191
)
9292
{
9393
if (aw == NULL || ctx == NULL) {
94+
PyErr_Format(
95+
PyExc_TypeError,
96+
"PyAwaitable: invalid NULL argument to PyAwaitable_AsyncWith"
97+
);
9498
return -1;
9599
}
96100
PyObject *with = PyObject_GetAttrString(ctx, "__aenter__");

0 commit comments

Comments
 (0)