Home
Tags Projects About
Resolve cython and numpy dependencies on setup step

Resolve cython and numpy dependencies on setup step

In my project, I have a custom Python package where part of the functionality is implemented with Cython to accelerate the slowest parts of the code. The syntax is very close to regular Python (in fact, it can be identical except for some imports), but Cython-compiled .so libraries allow direct access to low-level NumPy arrays.

The problem: Cython and NumPy need to be installed before setup.py starts running.

Here's what setup.py looks like:

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

setup(
    name=...,
    version=...,
    ...
    ext_modules=cythonize([
        Extension('package.cython_code1', ['package/cython_code1.pyx']),
        Extension('package.cython_code2', ['package/cython_code2.pyx']),
    ]),
    include_dirs=[numpy.get_include()],
)

There are a few ways to handle installing Cython and NumPy dependencies before setup.py execution:

Option 1: setup_requires

Starting with setuptools version 18.0 (released 2015-06-23), you can specify Cython in setup_requires and use .pyx modules directly as sources for setuptools.Extension:

from setuptools import setup, Extension

setup(
    ...
    setup_requires=[
        'setuptools>=18.0',  # Setuptools 18.0+ properly handles Cython extensions
        'cython',
    ],
    ext_modules=[
        Extension('package.cython_code1', sources=['package/cython_code1.pyx']),
        Extension('package.cython_code2', sources=['package/cython_code2.pyx']),
    ],
    include_dirs=[numpy.get_include()],
)

Note: If your installation tool is outdated and includes Cython in setup_requires, it may raise an ImportError like this:

ImportError: No module named Cython.Build

To address this, you can wrap the cythonize function in a simple closure to delay the import:

try:
    from Cython.Build import cythonize
except ImportError:
    # Create a closure for deferred import
    def cythonize(*args, **kwargs):
        from Cython.Build import cythonize
        return cythonize(*args, **kwargs)

With this, setup_requires will process and install Cython first, allowing cythonize to import successfully afterward.

Alternative

Issue 5761 on GitHub

Another approach is to install Cython and NumPy through setuptools.dist before running the setup script:

from setuptools import dist
dist.Distribution().fetch_build_eggs(['Cython>=0.15.1', 'numpy>=1.10'])

This ensures Cython and NumPy dependencies are fetched early and allows setup.py to run without ImportErrors.



Buy me a coffee

More? Well, there you go:

Interning in CPython

Pip constraints files

Python resource limitation