11

I'm writing a Cython 0.23 program, and I can't figure out how to use a cdef class that I import from a different module in a type declaration. Here is a snippet that reproduces the problem.

test.py:

import pyximport
pyximport.install()

from mymodule import *

obj = MyClass(42)
print(obj.field)
print(identity(obj).field)

This works as expected and prints 42 twice:

mymodule.pyx:

cdef class MyClass:
    cdef readonly int field
    def __init__(self, field):
        self.field = field

cpdef MyClass identity(MyClass obj):
    return obj

This fails with a compiler error:

mymodule.pyx:

from utils import MyClass

cpdef MyClass identity(MyClass obj):
    return obj

utils.pyx:

cdef class MyClass:
    cdef readonly int field
    def __init__(self, field):
        self.field = field

The error:

Error compiling Cython file:
------------------------------------------------------------
...
from utils import MyClass

cpdef MyClass identity(MyClass obj):
     ^
------------------------------------------------------------

mymodule.pyx:3:6: 'MyClass' is not a type identifier

Error compiling Cython file:
------------------------------------------------------------
...
from utils import MyClass

cpdef MyClass identity(MyClass obj):
                      ^
------------------------------------------------------------

The project I need this for is small and I can refactor it so that I don't need to import the class, but this solution doesn't look very clean. Is there a better way?

Pastafarianist
  • 833
  • 11
  • 27

1 Answers1

12

You need to use a declaration ".pxd" file and cimport. (Essentially, cimport happens at compile time, while import happens at run time so Cython can't make use of anything imported).

Create "utils.pxd":

cdef class MyClass:
    cdef readonly int field

"utils.pyx" now reads

cdef class MyClass:
  def __init__(self, field):
    self.field = field

(i.e. remove the declaration of field since it's specified in the .pxd file).

Then in mymodule.pyx

from utils cimport MyClass
# other code follows...
DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Thank you! Is there a way to keep the class definition in one place? – Pastafarianist Nov 09 '15 at 20:48
  • I don't think so (and I agree - this is a bit of a hassle with this method). – DavidW Nov 09 '15 at 20:55
  • @DavidW - From the docs here - https://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html do we need cdef cppclass now ? I obviously will ask my own question because I am getting the same compilation error – gansub Apr 28 '19 at 11:02
  • `cdef cppclass` is used when wrapping classes defined in c++. `cdef class` defines a class you can use from Python but with Cython type definitions, so they do different things. In both cases they can be cimported to use them in another Cython module. – DavidW Apr 28 '19 at 11:50