sortednp is my first open-source Python package which uses Python’s and Numpy’s C-API. There are huge ecosystems for testing Python libraries and C or C++ libraries. However, I found it quite cumbersome to test the C extension. This article is a short summary of the steps I took to have low-level tests of the C extension in a manylinux environment and high-level tests of the Python API provided by sortednp.

The first step is to build the actual extension. For this, it is convenient to use pip wheel to build a package including all binary libraries. Python is intended as a cross-platform language. This means, the build artifacts (might) depend on external libraries (and the os) of the built environment. To minimize the number of potential dependencies, it is common to build the library in a manylinux environment that ships only with a minimal set of external, shared libraries. The build artifacts from a manylinux environment are supposed to run everywhere (ie., every Linux-like system). The built environment is available as a docker image. For sortednp, the built options are defined in setup.py. The whole procedure is automated in the CI pipeline.

pip wheel -w wheelhouse/ .

sortednp performs intersection and merging operations on sorted numpy arrays. This involves quite some pointer arithmetics (linear search, binary search and galloping search). To test the individual search algorithms, there are type-templated googletest test-cases. There is nothing really special about these test-cases.

The test cases need to be compiled independently from the library and then linked with the library and gtest. I think it makes a lot of sense to run these tests also in the manylinux environment. However, by definition, this environment does not ship with gtest. Furthermore, the way I’m about to link and compile, I want Python to be available as a shared library. This is also not foreseen in the manylinux environment, in order to prevent any dependence on Python as a shared library. For our testing endeavor, this is not an issue. I’m not concerned about the portability of the test binary (It is even recommended to recompile gtest on every system). Remember the actual sortednp library is compiled in the default manylinux environment. So, to have the required environment, I’ve forked the manylinux project to provide a somelinux image with Python as a shared library and a gtest image with googletest support. These images are used in the CI pipeline to compile and link the test-cases.

GTEST="/usr/local/include/gtest"
NUMPY=$(python -c "import numpy; print(numpy.get_include())")

# compile
g++ -c ci/cxxtest.cpp -I src -I${NUMPY} $(python-config --cflags) -I${GTEST} -Wno-conversion-null -o cxxtest.o

# link
unzip -j wheelhouse/sortednp-*.whl "sortednp/_internal.*.so"
g++  cxxtest.o $(python-config --ldflags) -lgtest _internal*.so -o cxxtest

The final step is to run the executable generated in the previous step. The result can look like this.

LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(pwd) ./cxxtest