Matthew Moisen
Matthew Moisen

Reputation: 18299

How to set the name argument while using ctypes.pythonapi.PyCapsule_New

How do I pass in the name of a capsule into the second argument of PyCapsule_New via ctypes?

I've tried the following, but it seems that something is off:

capsule = ctypes.pythonapi.PyCapsule_New(b'data', b'abcdef.ghijkl', None)
capsule = ctypes.pythonapi.PyCapsule_New(b'data', ctypes.create_string_buffer(b"abcdef.ghijkl"), None)
capsule = ctypes.pythonapi.PyCapsule_New(b'data', ctypes.c_chap_p(b"abcdef.ghijkl"), None)

For example, the name of the capsule is not being set correctly:

>>> import ctypes
>>> ctypes.pythonapi.PyCapsule_New.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p]
>>> ctypes.pythonapi.PyCapsule_New.restype = ctypes.py_object
>>>
>>> capsule = ctypes.pythonapi.PyCapsule_New(b'data', b'abcdef.ghijkl', None)
>>>
>>> capsule
<capsule object "" at 0xf7102f98>
>>> capsule
<capsule object "e" at 0xf7102f98>
>>> capsule
<capsule object "���capsule
<capsule object "" at 0xf7102f98>
>>> capsule
<capsule object "e" at 0xf7102f98>
>>> capsule
<capsule object "e" at 0xf7102f98>
>>> capsule
<capsule object "���

If I use ctypes.create_string_buffer(b"abcdef.ghijkl"):

>>> capsule = ctypes.pythonapi.PyCapsule_New(b'data', ctypes.create_string_buffer(b"abcdef.ghijkl"), None)
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf7102fb0>
>>> capsule
<capsule object "" at 0xf7102fb0>
>>> capsule
<capsule object "" at 0xf7102fb0>
>>> capsule
<capsule object "" at 0xf7102fb0>
>>> capsule
<capsule object "" at 0xf7102fb0>

The strange thing here is that if I make a C Extension that returns a capsule, I can extract it's name using ctypes, and it works:

>>> capsule_from_c_ext = mycext.capsule()
>>> ctypes.pythonapi.PyCapsule_GetName.restype = ctypes.c_char_p
>>> ctypes.pythonapi.PyCapsule_GetName.argtypes = [ctypes.py_object]
>>> name = ctypes.pythonapi.PyCapsule_GetName(capsule_from_c_ext)
>>> name
b"abcdef.ghijkl"
>>> type(name)
<class 'bytes'>
>>> assert name == b"abcdef.ghijkl"
>>>
>>> capsule = ctypes.pythonapi.PyCapsule_New(b'data', name, None)
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23c20>
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23c20>
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23c20>
>>>
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23c20>
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23c20>

Additionally, if you use a shorter name, like b'name', it appears to work correctly.

Upvotes: 0

Views: 480

Answers (1)

Matthew Moisen
Matthew Moisen

Reputation: 18299

I got a hint from the docs:

The name string may either be NULL or a pointer to a valid C string. If non-NULL, this string must outlive the capsule. (Though it is permitted to free it inside the destructor.)

Creating a variable with the name first, and then calling it, seems to work:

>>> import ctypes
>>> ctypes.pythonapi.PyCapsule_New.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p]
>>> ctypes.pythonapi.PyCapsule_New.restype = ctypes.py_object
>>> name = b'abcdef.ghijkl'
>>> capsule = ctypes.pythonapi.PyCapsule_New(b'data', name, None)
>>>
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23bd8>
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23bd8>
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23bd8>
>>> capsule
<capsule object "abcdef.ghijkl" at 0xf6f23bd8>

And you can prove that you need the name variable to not be deallocated:

del name
>>> capsule
<capsule object "" at 0xf7102fb0>
>>> capsule
<capsule object "" at 0xf7102fb0>
>>> capsule
<capsule object "" at 0xf7102fb0>
>>> capsule
<capsule object "" at 0xf7102fb0>

Upvotes: 1

Related Questions