[Python-Dev] Use of Cython

Stefan Behnel stefan_ml at behnel.de
Sat Aug 4 10:46:33 EDT 2018

Antoine Pitrou schrieb am 04.08.2018 um 15:57:
> Le 04/08/2018 à 15:13, Nick Coghlan a écrit :
>> It'd be *really* nice to at least be able to write some of the C API
>> tests directly in Cython rather than having to fiddle about with
>> splitting the test between the regrtest parts that actually define the
>> test case and the extension module parts that expose the interfaces
>> that we want to test.
> Actually, I think testing the C API is precisely the kind of area where
> you don't want to involve a third-party, especially not a moving target
> (Cython is actively maintained and generated code will vary after each
> new Cython release).  Besides, Cython itself calls the C API, which
> means you might end up involuntarily testing the C API against itself.
> If anything, testing the C API using ctypes or cffi would probably be
> more reasonable... assuming we get ctypes / cffi to compile everywhere,
> which currently isn't the case.

I agree that you would rather not want to let Cython (or another tool)
generate the specific code that tests a specific C-API call, but you could
still use Cython to get around writing the setup, validation and unittest
boilerplate code in C. Basically, a test could then look something like
this (probably works, although I didn't test it):

    from cpython.object cimport PyObject
    from cpython.list cimport PyList_Append

    def test_PyList_Append_on_empty_list():
        # setup code
        l = []
        assert len(l) == 0
        value = "abc"
        pyobj_value = <PyObject*> value
        refcount_before = pyobj_value.ob_refcnt

        # conservative test call, translates to the expected C code,
        # although with exception propagation if it returns -1:
        errcode = PyList_Append(l, value)

        # validation
        assert errcode == 0
        assert len(l) == 1
        assert l[0] is value
        assert pyobj_value.ob_refcnt == refcount_before + 1

If you don't want the exception handling, you can define your own
declaration of PyList_Append() that does not have it. But personally, I'd
rather use try-except in my test code than manually taking care of cleaning
up (unexpected) exceptions.

What we do in Cython, BTW, is write doctests in compiled ".pyx" files. That
allows us to execute certain parts of a test in Python (the doctest code)
and other parts in Cython (the compiled functions/classes that have the
doctests), and thus to do a direct comparison between Python and Cython. An
example that you could find in a test ".pyx" file:

    def test_times2(x):
        doctest that gets executed by Python:

        >>> test_times2(3) == 3 * 2
        # Cython compiled code in a compiled function that gets tested:
        return x * 2

Given that CPython's current "_testcapimodule.c" is only a good 5000 lines
long (divide that by the number of public C-API functions and macros!), I'm
sure the above could help in improving the unit test coverage of the C-API
quite quickly.


More information about the Python-Dev mailing list