Sharing Declarations Between Pyrex Modules

This section describes a new set of facilities introduced in Pyrex 0.8 for making C declarations, functions and extension types in one Pyrex module available for use in another Pyrex module. These facilities are closely modelled on the Python import mechanism, and can be thought of as a compile-time version of it.

Contents

Definition and Implementation files

A Pyrex module can be split into two parts: a definition file with a .pxd suffix, containing C declarations that are to be available to other Pyrex modules, and an implementation file with a .pyx suffix, containing everything else. When a module wants to use something declared in another module's definition file, it imports it using the cimport statement.

What a Definition File contains

A definition file can contain: It cannot contain any non-extern C variable declarations.

It cannot contain the implementations of any C or Python functions, or any Python class definitions, or any executable statements.

NOTE: You don't need to (and shouldn't) declare anything in a declaration file public in order to make it available to other Pyrex modules; its mere presence in a definition file does that. You only need a public declaration if you want to make something available to external C code.

What an Implementation File contains

An implementation file can contain any kind of Pyrex statement, although there are some restrictions on the implementation part of an extension type if the corresponding definition file also defines that type (see below).

The cimport statement

The cimport statement is used in a definition or implementation file to gain access to names declared in another definition file. Its syntax exactly parallels that of the normal Python import statement:
cimport module [, module...]
from module cimport name [as name] [, name [as name] ...]
Here is an example. The file on the left is a definition file which exports a C data type. The file on the right is an implementation file which imports and uses it.
 
dishes.pxd restaurant.pyx
cdef enum otherstuff:
    sausage, eggs, lettuce

cdef struct spamdish:
    int oz_of_spam
    otherstuff filler

cimport dishes
from dishes cimport spamdish

cdef void prepare(spamdish *d):
    d.oz_of_spam = 42
    d.filler = dishes.sausage

def serve():
    cdef spamdish d
    prepare(&d)
    print "%d oz spam, filler no. %d" % \
         (d.oz_of_spam, d.filler)

It is important to understand that the cimport statement can only be used to import C data types, C functions and variables, and extension types. It cannot be used to import any Python objects, and (with one exception) it doesn't imply any Python import at run time. If you want to refer to any Python names from a module that you have cimported, you will have to include a regular import statement for it as well.

The exception is that when you use cimport to import an extension type, its type object is imported at run time and made available by the name under which you imported it. Using cimport to import extension types is covered in more detail below.

Search paths for definition files

When you cimport a module called modulename, the Pyrex compiler searches for a file called modulename.pxd along the search path for include files, as specified by -I command line options.

Also, whenever you compile a file modulename.pyx, the corresponding definition file modulename.pxd is first searched for along the same path, and if found, it is processed before processing the .pyx file.

Using cimport to resolve naming conflicts

The cimport mechanism provides a clean and simple way to solve the problem of wrapping external C functions with Python functions of the same name. All you need to do is put the extern C declarations into a .pxd file for an imaginary module, and cimport that module. You can then refer to the C functions by qualifying them with the name of the module. Here's an example:
 
c_lunch.pxd lunch.pyx
cdef extern from "lunch.h":
    void eject_tomato(float)
cimport c_lunch

def eject_tomato(float speed):
    c_lunch.eject_tomato(speed)

You don't need any c_lunch.pyx file, because the only things defined in c_lunch.pxd are extern C entities. There won't be any actual c_lunch module at run time, but that doesn't matter; the c_lunch.pxd file has done its job of providing an additional namespace at compile time.

Sharing C Functions

C functions defined at the top level of a module can be made available via cimport by putting headers for them in the .pxd file, for example,

volume.pxd
spammery.pyx
cdef float cube(float)
from volume cimport cube

def menu(description, size):
    print description, ":", cube(size), \
"cubic metres of spam"

menu("Entree", 1)
menu("Main course", 3)
menu("Dessert", 2)
volume.pyx
cdef float cube(float x):
    return x * x * x

Sharing Extension Types

An extension type can be made available via cimport by splitting its definition into two parts, one in a definition file and the other in the corresponding implementation file.

The definition part of the extension type can only declare C attributes and C methods, not Python methods, and it must declare all of that type's C attributes and C methods.

The implementation part must implement all of the C methods declared in the definition part, and may not add any further C attributes or methods. It may also define Python methods.

Here is an example of a module which defines and exports an extension type, and another module which uses it.
 
Shrubbing.pxd Shrubbing.pyx
cdef class Shrubbery:
    cdef int width
    cdef int length
cdef class Shrubbery:
    def __cinit__(self, int w, int l):
        self.width = w
        self.length = l

def standard_shrubbery():
    return Shrubbery(3, 7)

Landscaping.pyx
cimport Shrubbing
import Shrubbing

cdef Shrubbing.Shrubbery sh
sh = Shrubbing.standard_shrubbery()
print "Shrubbery size is %d x %d" % (sh.width, sh.length)
 

Some things to note about this example:

If you are exporting an extension type that has a base class, the base class must be declared in the definition part. Repeating the base class in the implementation part is not necessary, but if you do, it must match the base class in the definition part.

Circular cimports

If you have two structs, unions or extension types defined in different .pxd files, and they need to refer to each other, there is a potential for problems with circular imports. These problems can be avoided by placing forward declarations of all the structs, unions and extension types defined in the .pxd file before the first cimport statement.

For example:

foo.pxdblarg.pxd
cdef struct Spam

from blarg cimport Eggs

cdef struct Spam:
    Eggs *eggs
cdef struct Eggs

from foo cimport Spam

cdef struct Eggs:
    Spam *spam

If the forward declarations weren't present, a circular import problem would occur, analogous to that which arises in Python when two modules try to import names from each other. Placing the forward declarations before the cimport statements ensures that all type names are known to the Pyrex compiler sufficiently far in advance.

Note that any .pyx file is free to cimport anything it wants from any .pxd file without needing this precaution. It's only when two .pxd files import each other that circular import issues arise.
Back to the Language Overview