[Cython] Unit testing Cython extensions

Robert Bradshaw robertwb at gmail.com
Tue Apr 23 23:07:14 CEST 2013


There is nothing special about Cython here, if you have "foo.py"
instead of "foo.so" you would be seeing exactly the same results. You
need to either set PYTHONPATH or have foo be in the same package
layout as your tests.

On Tue, Apr 23, 2013 at 1:51 PM, Torsten Landschoff
<torsten.landschoff at dynamore.de> wrote:
> Hi again,
>
> I am just wondering how to do unit testing on extensions built using Cython
> with py.test.
>
> My problem: The extension module I am working on is installed in our global
> python environment (inside the build slaves as well as on local machines)
> already. Before installing a new version of the extension module, I'd like
> to run the unit tests. However, that way the original module is tested.
>
> I am trying to illustrate.
>
> Doing TDD I create a new module which will fail the test:
>
>
> torsten at sharokan:~/foo$ cat foo.pyx
> def it_works():
>     return False
>
> torsten at sharokan:~/foo$ cat tests/test_foo.py
> import pprint, foo
>
> def test_foo():
>     pprint.pprint(foo.__file__)
>     assert foo.it_works()
>
> torsten at sharokan:~/foo$ py.test -v tests
> ============================================================ test session
> starts ============================================================
> platform linux2 -- Python 2.7.3 -- pytest-2.3.4 --
> /opt/dynasdk/loco2-precise/bin/python
> plugins: cov, capturelog
> collected 0 items / 1 errors
>
> ================================================================== ERRORS
> ===================================================================
> ____________________________________________________ ERROR collecting
> tests/test_foo.py _____________________________________________________
> tests/test_foo.py:1: in <module>
>>   import sys, pprint, foo
> E   ImportError: No module named foo
> ========================================================== 1 error in 0.01
> seconds ==========================================================
>
> Sure, module foo does not exists. I could use pyximport, but I want to check
> that the extension itself is correctly built.
> Easily done:
>
> torsten at sharokan:~/foo$ python setup.py build_ext -i
> running build_ext
> cythoning foo.pyx to foo.c
> building 'foo' extension
> creating build
> creating build/temp.linux-x86_64-2.7
> gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall
> -Wstrict-prototypes -fPIC -I/opt/dynasdk/loco2-precise/include/python2.7 -c
> foo.c -o build/temp.linux-x86_64-2.7/foo.o
> gcc -pthread -shared build/temp.linux-x86_64-2.7/foo.o
> -L/opt/dynasdk/loco2-precise/lib -lpython2.7 -o /home/torsten/foo/foo.so
>
> But alas, py.test still will not find foo.so, which is now installed into
> the current directory (due to using the "-i" flag to build_ext). Workaround:
> Overwrite PYTHONPATH:
>
> torsten at sharokan:~/foo$ PYTHONPATH=`pwd` py.test
> ============================================================ test session
> starts ============================================================
> platform linux2 -- Python 2.7.3 -- pytest-2.3.4
> plugins: cov, capturelog
> collected 1 items
>
> tests/test_foo.py F
>
> ================================================================= FAILURES
> ==================================================================
> _________________________________________________________________ test_foo
> __________________________________________________________________
>
>     def test_foo():
>         pprint.pprint(foo.__file__)
>>       assert foo.it_works()
> E       assert <built-in function it_works>()
> E        +  where <built-in function it_works> = foo.it_works
>
> tests/test_foo.py:5: AssertionError
> -------------------------------------------------------------- Captured
> stdout --------------------------------------------------------------
> '/home/torsten/foo/foo.so'
> ========================================================= 1 failed in 0.02
> seconds ==========================================================
>
> Now it really loads our extension module and the failure is actually
> genuine. Let's assume we have the old version installed in our current
> Python environment:
>
> torsten at sharokan:~/foo$ python setup.py install
> [...]
> creating
> /usr/opt/dynasdk/loco2-precise/lib/python2.7/site-packages/foo-0.0-py2.7-linux-x86_64.egg
> Extracting foo-0.0-py2.7-linux-x86_64.egg to
> /usr/opt/dynasdk/loco2-precise/lib/python2.7/site-packages
> Adding foo 0.0 to easy-install.pth file
>
> Installed
> /usr/opt/dynasdk/loco2-precise/lib/python2.7/site-packages/foo-0.0-py2.7-linux-x86_64.egg
> [...]
>
> Great. Now let's implement the feature that our test checks:
>
> torsten at sharokan:~/foo$ vim foo.pyx
> torsten at sharokan:~/foo$ cat foo.pyx
> def it_works():
>     return True
>
> The unit tests should pass now, after we rebuilt the extension:
>
> torsten at sharokan:~/foo$ python setup.py build_ext -i
> running build_ext
> cythoning foo.pyx to foo.c
> building 'foo' extension
> gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall
> -Wstrict-prototypes -fPIC -I/opt/dynasdk/loco2-precise/include/python2.7 -c
> foo.c -o build/temp.linux-x86_64-2.7/foo.o
> gcc -pthread -shared build/temp.linux-x86_64-2.7/foo.o
> -L/opt/dynasdk/loco2-precise/lib -lpython2.7 -o /home/torsten/foo/foo.so
> torsten at sharokan:~/foo$ PYTHONPATH=`pwd` py.test
> ============================================================ test session
> starts ============================================================
> platform linux2 -- Python 2.7.3 -- pytest-2.3.4
> plugins: cov, capturelog
> collected 1 items
>
> tests/test_foo.py F
>
> ================================================================= FAILURES
> ==================================================================
> _________________________________________________________________ test_foo
> __________________________________________________________________
>
>     def test_foo():
>         pprint.pprint(foo.__file__)
>>       assert foo.it_works()
> E       assert <built-in function it_works>()
> E        +  where <built-in function it_works> = foo.it_works
>
> tests/test_foo.py:5: AssertionError
> -------------------------------------------------------------- Captured
> stdout --------------------------------------------------------------
> '/opt/dynasdk/loco2-precise/lib/python2.7/site-packages/foo-0.0-py2.7-linux-x86_64.egg/foo.so'
> ========================================================= 1 failed in 0.02
> seconds ==========================================================
>
> Unfortunately, it doesn't. The unit tests actually uses the installed
> version of the library, which we only want to replace after our tests pass.
>
> Let's try it another way: By creating a virtualenv just for our tests, we
> should be fine:
>
> torsten at sharokan:~/foo$ virtualenv --system-site-packages fooenv
> New python executable in fooenv/bin/python
> Please make sure you remove any previous custom paths from your
> /home/torsten/.pydistutils.cfg file.
> Installing setuptools............done.
> Installing pip...............done.
> torsten at sharokan:~/foo$ . fooenv/bin/activate
> (fooenv)torsten at sharokan:~/foo$ pip install --upgrade .
> Unpacking /home/torsten/foo
>   Running setup.py egg_info for package from file:///home/torsten/foo
>
> Downloading/unpacking Cython from
> http://pypi.python.org/packages/source/C/Cython/Cython-0.19.tar.gz#md5=76989337dee4cf7afdcb5cde514423f8
> (from foo==0.0)
>   Downloading Cython-0.19.tar.gz (1.4MB): 270kB downloaded
> ...
>
> Good start, but I don't want to recreate the whole thing inside the
> virtualenv fooenv. This would pull Cython, numpy, scipy, paramiko and more.
>
> Any hint how to ensure that we are testing the local version of that
> extension instead of the installed one?
>
> Thanks, Torsten
>
> --
> DYNAmore Gesellschaft fuer Ingenieurdienstleistungen mbH
> Torsten Landschoff
>
> Office Dresden
> Tel: +49-(0)351-4519587
> Fax: +49-(0)351-4519561
>
> mailto:torsten.landschoff at dynamore.de
> http://www.dynamore.de
>
> DYNAmore Gesellschaft für FEM Ingenieurdienstleistungen mbH
> Registration court: Stuttgart, HRB 733694
> Managing director: Prof. Dr. Karl Schweizerhof, Dipl.-Math. Ulrich Franz
>
>
> _______________________________________________
> cython-devel mailing list
> cython-devel at python.org
> http://mail.python.org/mailman/listinfo/cython-devel
>


More information about the cython-devel mailing list