UncleBob
UncleBob

Reputation: 1391

Python, PyPy and CFFI: what am I supposed to use?

I need to call C libraries with python, and after some consideration it seemed CFFI would be most adequate for the kind of job. By now, however, I am thouroughly confused if I'm using it right, because some things seem to work as intended only on PyPy, while others seem to work only on Python3 (which PyPy doesn't support, as far as I know).

Here's the most basic code sample from the CFFI documentation:

>>> from cffi import FFI
>>> ffi = FFI()
>>> ffi.cdef("""
...     int printf(const char *format, ...);   // copy-pasted from the man page
... """)
>>> C = ffi.dlopen(None)                     # loads the entire C namespace
>>> arg = ffi.new("char[]", "world")         # equivalent to C code: char arg[] = "world";
>>> C.printf("hi there, %s.\n", arg)         # call printf
hi there, world.
17                                           # this is the return value
>>>

When running this code with Python3, I get the following error: TypeError: initializer for ctype 'char[]' must be a bytes or list or tuple, not str

Looking for the error, I found it as an issue that was fixed in January last year in PyPy. So I see if the thing runs with PyPy, and it does. Hooray!

However, already in the second example, I'm getting the problem in reverse:

# file "simple_example_build.py"

# Note: this particular example fails before version 1.0.2
# because it combines variadic function and ABI level.

from cffi import FFI

ffi = FFI()
ffi.set_source("_simple_example", None)
ffi.cdef("""
    int printf(const char *format, ...);
""")

if __name__ == "__main__":
    ffi.compile()

Running this in PyPy throws another error at me:

AttributeError: 'FFI' object has no attribute 'set_source'

Since the example states helpfully that it won't work on older versions, I checked my CFFI version: 1.2.1, everything's alright.

Finally I run the second example with Python3 instead of PyPy, and who would have thought, it does exactly what it's supposed to do.

Being new to Python, by now I don't really have a clue anymore what I am supposed to use, and why the examples from the same documentation only run on different versions of the language. And there's of course still the problem that I might just have configured something wrong (what with being new to Linux too), or that I should use another python version altogether. Could somebody shed some light on this?

Upvotes: 1

Views: 2196

Answers (2)

huangjiansword
huangjiansword

Reputation: 1

you can modify it as below:

from cffi import FFI
ffi = FFI()
ffi.cdef("""int printf(const char *format, ...);""")
C = ffi.dlopen(None)                     # loads the entire C namespace
arg = ffi.new("char[]", b"world")         # equivalent to C code: char arg[]     = "world";
C.printf(b"hi there, %s.\n", arg)         # call printf

Upvotes: 0

Armin Rigo
Armin Rigo

Reputation: 12900

When running this code with Python3, I get the following error: TypeError: initializer for ctype 'char[]' must be a bytes or list or tuple, not str

Yes, because for Python 3 you need to use the 'b' prefix to make sure you are handling bytes. The example in the docs states this clearly just below.

AttributeError: 'FFI' object has no attribute 'set_source'

This means that you have an old version of PyPy. The version of CFFI cannot be upgraded in a given version of PyPy; you need to upgrade PyPy itself. Check which CFFI version your particular PyPy comes with like this:

$ pypy
Python 2.7.9 (295ee98b6928, May 31 2015, 07:29:04)
[PyPy 2.6.0 with GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>> import cffi
>>>> cffi.__version__
'1.1.0'

I'm pretty sure you're using an older version of PyPy that comes with an older version of CFFI, not 1.2.1.

Upvotes: 2

Related Questions