Skip to content

Commit 768722f

Browse files
committed
Add PyCapsule_New and PyCapsule_GetPointer
These are critical when interacting with PyCapsules created by other libraries in the system. The current use case is interacting with PyArrow. Signed-off-by: Marius Seritan <[email protected]>
1 parent 77a549c commit 768722f

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

mojo/stdlib/src/python/_cpython.mojo

+36
Original file line numberDiff line numberDiff line change
@@ -1929,3 +1929,39 @@ struct CPython:
19291929

19301930
self._inc_total_rc()
19311931
return r
1932+
1933+
# ===-------------------------------------------------------------------===#
1934+
# Capsules
1935+
# ref: https://docs.python.org/3/c-api/capsule.html
1936+
# ===-------------------------------------------------------------------===#
1937+
1938+
fn PyCapsule_New(
1939+
mut self,
1940+
pointer: OpaquePointer,
1941+
name: StringSlice,
1942+
destructor: destructor,
1943+
) -> PyObjectPtr:
1944+
"""Create a PyCapsule to communicate to another C extension the C API in `pointer`, identified by `name` and with the custom destructor in `destructor`.
1945+
1946+
[Reference](https://docs.python.org/3/c-api/capsule.html#c.PyCapsule_New).
1947+
"""
1948+
# PyObject *PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
1949+
var new_capsule = self.lib.call["PyCapsule_New", PyObjectPtr](
1950+
pointer, name.unsafe_ptr().bitcast[c_char](), destructor
1951+
)
1952+
self._inc_total_rc()
1953+
return new_capsule
1954+
1955+
fn PyCapsule_GetPointer(
1956+
mut self,
1957+
capsule: PyObjectPtr,
1958+
name: StringSlice,
1959+
) -> OpaquePointer:
1960+
"""Extract the pointer to another C extension from a PyCapsule `capsule` with the given `name`.
1961+
1962+
[Reference](https://docs.python.org/3/c-api/capsule.html#c.PyCapsule_GetPointer).
1963+
"""
1964+
# void *PyCapsule_GetPointer(PyObject *capsule, const char *name)
1965+
return self.lib.call["PyCapsule_GetPointer", OpaquePointer](
1966+
capsule, name.unsafe_ptr().bitcast[c_char]()
1967+
)

mojo/stdlib/test/python/test_python_cpython.mojo

+29
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
# RUN: %mojo %s
1515

1616
from python import Python, PythonObject
17+
from python._cpython import PyObjectPtr
18+
from memory import UnsafePointer
1719
from testing import assert_equal, assert_false, assert_raises, assert_true
1820

1921

@@ -34,7 +36,34 @@ def test_PyObject_HasAttrString(mut python: Python):
3436
_ = the_object
3537

3638

39+
fn destructor(capsule: PyObjectPtr) -> None:
40+
pass
41+
42+
43+
def test_PyCapsule(mut python: Python):
44+
var Cpython_env = python.impl._cpython
45+
46+
# Not a PyCapsule, a NULL pointer is expected.
47+
var the_object = PythonObject(0)
48+
var result = Cpython_env[].PyCapsule_GetPointer(
49+
the_object.py_object, "some_name"
50+
)
51+
var expected = UnsafePointer[NoneType]()
52+
assert_equal(expected, result)
53+
54+
# Build a capsule.
55+
var capsule_impl = UnsafePointer[UInt64].alloc(1)
56+
var capsule = Cpython_env[].PyCapsule_New(
57+
capsule_impl.bitcast[NoneType](), "some_name", destructor
58+
)
59+
var capsule_pointer = Cpython_env[].PyCapsule_GetPointer(
60+
capsule, "some_name"
61+
)
62+
assert_equal(capsule_impl.bitcast[NoneType](), capsule_pointer)
63+
64+
3765
def main():
3866
# initializing Python instance calls init_python
3967
var python = Python()
4068
test_PyObject_HasAttrString(python)
69+
test_PyCapsule(python)

0 commit comments

Comments
 (0)