Felix Hohnstein
Felix Hohnstein

Reputation: 549

Cython: How to cimport your own cython modules within a package?

I am creating a cython package where several modules share extension types with each other. However I have problems getting the cimports right.

I have created a simple example project to show case my problem. As you can see: All cython files are within a package call myPackage.

myProject
│
├── main.py
│
├── myPackage
│   ├── animal.pxd
│   ├── animal.pyx 
│   ├── zoo.pxd
│   ├── zoo.pyx 
│   ├── setup.py

Can you explain to me, how to use cimport such that zoo.pyx has knowledge about the extension types in animal.pyx ?

This is the animal.pyx file ...

cdef class Animal:
    def __init__(self, str name, double weight):
        self.name = name
        self.weight = weight

This is the animal.pxd file ...

cdef class Animal:
    cdef:
        public str name
        public double weight

This is the zoo.pyx file ...

from myPackage.animal cimport Animal

cdef class Zoo:
    def __init__(self):
        self.animals_cage = []
        print('ready!')

    cpdef void add_animal(self, Animal animal):
        self.animals_cage.append(animal)

This is the zoo.pxd file ...

from myPackage.animal cimport Animal 

cdef class Zoo:
    cdef list animals_cage

    cpdef void add_animal(self, Animal animal)

The setup is done by ...

from distutils.core import setup, Extension
from Cython.Build import cythonize

ext_modules = [Extension(name="animal", sources=["animal.pyx"]),
               Extension(name="zoo", sources=["zoo.pyx"])]

setup(ext_modules=cythonize(ext_modules))

The main.py is as basic as it can get ...

from myPackage.animal import Animal
from myPackage.zoo import Zoo

if __name__ == '__main__':
    a = Animal("bob", 4)
    z = Zoo()  

Trials and Results:
1st Trail:

Run setup.py inside myPackage. Run main.

compiling failed    
'Animal' is not a type identifier

(my guess) cython could not find myPackage.animal.pyx

2nd Trial:
Add include_path=['myPackage'] to cythonize call. Run setup.py inside myPackage. Run main.py

compiling works but main.py produces:    
AttributeError: 'zoo.Zoo' object has no attribute 'cage'

3rd Trial
make all cimports relative ( aka. from animal cimport Animal) and remove include_dirs from setup.py

compiling works and main.py works    

However, if you switch the imports in main.py. Making Zoo the 1st import:

from myPackage.zoo import Zoo
from myPackage.animal import Animal

if __name__ == '__main__':
    a = Animal("bob", 4)
    z = Zoo()

ModuleNotFoundError: No module named 'animal'

Upvotes: 3

Views: 995

Answers (1)

Felix Hohnstein
Felix Hohnstein

Reputation: 549

To get main.py working, I had to:

  • move setup.py from myPackage to myProject and change Paths accordingly
  • not use include_dir or include_path
  • cimport: from myPackage.animal cimport Animal

Thou I have no Idea why it does not work when setup.py is inside myPackage!

myProject
│
├── main.py
├── setup.py
│
├── myPackage
│   ├── animal.pxd
│   ├── animal.pyx 
│   ├── zoo.pxd
│   ├── zoo.pyx 

and the setup.py

from setuptools import setup, find_packages, Extension
from Cython.Build import cythonize

extensions = [
    Extension("myPackage.animal", ["myPackage/animal.pyx"]),
    Extension("myPackage.zoo", ["myPackage/zoo.pyx"])]

compiler_directives = {"language_level": 3, "embedsignature": True}
extensions = cythonize(extensions, compiler_directives=compiler_directives) 

Upvotes: 2

Related Questions