Search code examples
pythoncython

Can pyximport.install() of Cython build more than 2 .pyx files simultaneously without setup.py?


  1. (1) Can pyximport.install() of Cython build more than 2 cython files(.pyx) simultaneously without setup.py?
  2. (2) If (1) can do, can the abstract class which is separated by implemented in the mechanism (1)?

MyCode

I want to implement the abstract class in .pyx files. And my python code (written in abst_cls2main.py) wants to import a class inherited Child(written in abst_cls2child.pyx) the abstract class Base(written in abst_cls2base.pyx).

# FILENAME: abst_cls2child.pyx
#cython: language_level=3
# distutils: language = c++

cimport cython
# from abst_cls1base cimport Base
# from abst_cls1base import Base
# cimport abst_cls1base
import pyximport;

pyximport.install()
from abst_cls2base import Base
# from abst_cls2base cimport Base

print(f"Loading {__file__}")
cdef class Child(Base):
    def __init__(self):
        super().__init__()
        self.show()

print(f"Loaded {__file__}")
#FILENAME: abst_cls2base.pyx
#cython: language_level=3
# distutils: language = c++
import pyximport;

pyximport.install()
cdef class Base:
    cdef int a
    
    def show(self):
        print("Base class called")
# FILENAME: abst_cls2main.py
import pyximport;

pyximport.install()
# cimport cython
from abst_cls2child import Child

a = Child()

If the above files(abst_cls2child.pyx,abst_cls2base.pyx) are described into one .pyx file. The files(one.pyx, abst_cls2main.py) works just as I expected.


Solution

  • most of the info taken from cython documentation sharing declarations and cython documentation extension types

    you need to declare a header file for abst_cls2base.pyx with name abst_cls2base.pxd which defines the class

    # FILENAME: abst_cls2base.pxd
    # cython: language_level=3
    # distutils: language = c++
    
    cdef class Base:
        cdef int a
        cpdef show(self)
    

    now you need to modify the implementation of Base and remove the data attributes

    #FILENAME: abst_cls2base.pyx
    # cython: language_level=3
    # distutils: language = c++
    
    cdef class Base:
    
        cpdef show(self):
            self.a += 1
            print("a =",self.a)
            print("Base class called")
    

    and your second module needs to cimport it, when it sees the cimport it will look for the class in the header abst_cls2base.pxd and see it's an extension type

    # FILENAME: abst_cls2child.pyx
    #cython: language_level=3
    # distutils: language = c++
    
    from abst_cls2base cimport Base
    
    print(f"Loading {__file__}")
    cdef class Child(Base):
    
        def __init__(self):
            super().__init__()
            self.show()
            self.increment_a()
            
        cpdef increment_a(self):
            self.a += 1
            print("a =",self.a)
    
    print(f"Loaded {__file__}")
    

    and for your main python script

    # FILENAME: abst_cls2main.py
    import pyximport
    
    pyximport.install()
    
    from abst_cls2child import Child
    a = Child()
    
    a = 1
    Base class called
    a = 2
    

    now the print statements in the global scope aren't executed ... mostly because cython code is compiled, not interpreted like python, it doesn't run in the same order that it is written, but the rest of the code works.

    just note that i converted the function show to cpdef and redeclared it in the header file so it can be called without invoking the interpreter within cython, converting it back to def will make it called through the interpreter which is against the point of having cython modules ...