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
This might also interest you