Using Python with qibuild projects¶
Introduction¶
Let’s say you have a C++ library in a qibuild project, called foo
.
<worktree>
|__ foo
|__ qiproject.xml
|__ CMakeLists.txt
|__ foo.cpp
|__ foo.hpp
# in <worktree>/foo/CMakeLists.txt
project(foo)
find_package(qibuild)
qi_create_lib(SHARED foo foo.hpp foo.cpp)
<!-- in <worktree>/foo/qiproject.xml -->
<project version="3">
<qibuild name="foo" />
</project>
You wish to write a C++ Python extension in another project to wrap the
foo
library, using the qibuild build system. Let’s say you use swig for
this.
You have an interface file for swig called pyfoo.i
which is going
to generate a _pyfoo.so
Python extension, and a foo.py
to wrap
the C++ extension.
<worktree>
|__ foo
|__ qiproject.xml
|__ CMakeLists.txt
|__ foo.cpp
|__ foo.hpp
|__ pyfoo
|__ qiproject.xml
|__ CMakeLists.txt
|__ pyfoo.i
|__ foo.py
|__ foo_script.py
<!-- in pyfoo/qiproject.xml -->
<project version="3">
<qibuild name="pyfoo">
<depends runtime="true" buildtime="true" names="foo" />
</qibuild>
<qipython name="pyfoo">
<setup with_distutils="true" />
</qipython>
</project>
# in <worktree>/foo-py/CMakeLists.txt
project(pyfoo)
qi_swig_python(_pyfoo pyfoo.i DEPENDS FOO)
// In pyfoo.i
%module _pyfoo
%{
#include "foo.hpp"
%}
%include "foo.hpp"
# In foo.py
import _pyfoo
...
# In foo_script.py
import foo
...
def main():
....
if __name__ == "__main__":
main()
You want to be able to build the pyfoo
extension, and use foo-script.py
directly without having to set PYTHONPATH
to something like:
<worktree>/pyfoo/build-linux64/sdk/lib
.
In order to do so, you can write a setup.py
for your python project
(pyfoo
, and use qipy
to run the script)
Under the cover, everything will be done using a virtualenv
and distutils
.
Some useful links:
Step one: Basic checks¶
Just make sure your project is listed when running qipy list
,
and that the extension is built:
qibuild configure pyfoo
qibuild make pyfoo
Step two: Write a setup.py file¶
# in pyfoo/setup.py
import os
from setuptools import setup
setup(name="mymodule",
version="0.1",
py_modules=['foo'],
entry_points = {
"console_scripts" : [
"pyfoo = foo_script:main"
]
}
)
If you do not want to use setup.py
, you can specify
your modules, packages and scripts directly in the
qiproject.xml
, like this:
qiproject.xml
lib
foo
__init__.py
bar.py
spam.py
bin
eggs
<project version="3">
<qipython name="foobar">
<package name="foo" src="lib" />
<module name="spam" />
<script src="bin/eggs" />
</qipython>
</project>
Step three: Use qipy bootstrap¶
qipy bootstrap
This will initialize a virtualenv in the worktree, and should be run when changing or adding new python projects.
You can use a -c
option to have several virtualenv in the worktree.
The virtualenv will be initialized using pip install --editable
, so you
will be able to run your python code directly from the sources.
Also, the qi_swig_python
CMake call will add the path to the extension library
in a qi.pth
file in the virtualenv.
Step four: Use the virtualenv¶
Just use qipy run
instead of python
qipy run [-c config] foo_script.py
If you have several commands to run, use something like
source $(qipy sourceme -q)
to activate the virtualenv in your
current session.
Step five: adding Python tests¶
It’s recommended to use pytest to run your tests.
You can do something like
cd /path/to/project
qipy run -- py.test -- [OTHER py.test args]
If you get a segmentation fault while running your tests
(which can happen when you write C++ Python extensions),
here is how to run pytest
inside gdb
:
source $(qipy sourceme -q)
gdb /path/to/worktree/.qi/venv/py-<config>/bin/python
run -m pytest