I am writing a a high level interface for a C library for Python using Cython.
I have an extension Type A
that initializes the library with a pointer to a more complex C context structure c_context
. The pointer is saved in A
.A
also has a def
function which in turn creates another extension Type B
initializing another C structure with a library function call. This structure is needed for the subsequent library calls made in B
.B
needs the c_context
pointer from A
which is wrapped by me within the extension type py_context
in order to pass it to __cinit__
from B
:
#lib.pxd (C library definitions)
cdef extern from "lib.h":
ctypedef struct c_context:
pass
#file py_context.pxd
from lib cimport c_context
cdef class py_context:
cdef c_context *context
cdef create(cls, c_context *context)
cdef c_context* get(self)
#file py_context.pyx
def class py_context:
@staticmethod
cdef create(cls, c_context *c):
cls = py_nfc_context()
cls.context = c
return cls
cdef c_context* get(self):
return self.context
Passing the wrapper with the correct C context works perfectly.
Now I need to get the C struct out of py_context
again and save it in B
. I added cdef c_context get(self)
to py_context.pxd/pyx
.
Calling py_context.get()
from Bs __cinit__
results in: AttributeError: py_context object has no attribute get.
It seems like I do not get my head around when to call cdef
functions in Cython.
So my question is: What is the best way to extract the C struct from my wrapper class again?
To make your Python into Cython, first you need to create a file with the . pyx extension rather than the . py extension. Inside this file, you can start by writing regular Python code (note that there are some limitations in the Python code accepted by Cython, as clarified in the Cython docs).
The Cython language is a superset of Python that compiles to C, yielding performance boosts that can range from a few percent to several orders of magnitude, depending on the task at hand. For work that is bound by Python's native object types, the speedups won't be large.
The cimport statement is used by Cython to import definitions from a . pxd file. This is different than using a normal Python import statement, which would load a regular Python module.
Cython is a compiled language that is typically used to generate CPython extension modules.
The trouble is that Cython doesn't know the data type of your py_context
variable at compile time. Calls to cdef
functions are resolved at compile time and there exists no mechanism to figure it out at runtime by attribute lookup (as with normal Python functions).
[Note that def
functions written within Cython are still compiled and can specify data types, so are perfectly capable of calling cdef
functions if they have the right information.]
You don't give the relevant code where it's going wrong (the constructor of type B
), but here's an very simplified example which will hopefully give you a couple of ways to fix it:
cdef class A:
cdef f(self):
return
def f1(var):
var.f()
#f1(A()) # will fail at runtime with an attribute error
In f1
the type of var
is unknown, and thus you can't call cdef
functions.
def f2(A var):
var.f()
f2(A()) # will work
f2(1) # will fail, int can't be converted to A
In f2
the type of var
is constrained to be A
, and therefore it can happily call cdef
functions associated with A
. If you pass something that isn't A
to it you'll get a TypeError
at runtime.
def f3(var):
cdef A another_reference_to_var = var # this does test that the types match
another_reference_to_var.f()
f3(A()) # will work
f3(1) # will fail, int can't be converted to A
The function f3
can take a variable of any type. However, when you assign it to another_reference_to_var
which is cdef
ed to be an A
it checks that the type matches (and raises a runtime exception if it doesn't). Since another_reference_to_var
is known to be A
at compile time, you can call A
s cdef
functions.
Essentially, you need to specify the type of the relevant input to your __cinit__
function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With