ctypes <https://docs.python.org/3/library/ctypes.html> is a wonderful library. If you need to create a Python binding for some library that doesn’t already have one, you should look at ctypes, rather than writing an extension module in C or C++. The odds are, a pure Python implementation using ctypes can be done with much less effort than writing C/C++ code.
But it is not enough to wrap an API designed for C or C++. A Python API should take advantage of the power and convenience of Python. The question I like to ask is: “How would the API have been designed if it was native to Python, rather than being intended for C or C++?”. Another question to consider is: “Does the Python API binding give you a reason to use Python to write your code in the first place, rather than choosing C or C++ instead?”. In short, does your Python code that makes calls to the API look like a straight transliteration of C code, or does it work at a higher level? What do I mean by this? An obvious, very basic, example is not having the user have to bother with explicitly creating and destroying objects. Wrap an underlying API object in a Python object, such that the Python object’s “__del__” method will take care of disposing of the API object. Then Python’s usual object-management mechanisms will automatically manage the API objects as well. This is how Qahirah <https://github.com/ldo/qahirah>, my wrapper for the Cairo graphics library <https://www.cairographics.org/>, works. But there is more you can do. Another obvious one is, where the underlying API provides “get_xxx” and “set_xxx” methods, to wrap them in a read/write Python property. For example, Cairo defines a “fill rule” that governs how paths are filled, with getter and setter calls <https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-fill-rule>. In Qahirah this is translated to a property that you can obtain with a simple expression like “«ctx».fill_rule” (where «ctx» is a Context object), and change by a simple assignment like “«ctx».fill_rule = «new_value»”. Sometimes the values that are returned by the getter or set with the setter have a more complex, variable-length structure. So the C API may need two parts to the getter routines, one to determine how big a structure the caller needs to allocate, and the other to fill in a preallocated structure with the actual data. For example, the dash setting for stroking lines <https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash> is of this type. In Python, it makes sense to store the dash information in a high-level Python object, which is directly returned from the Python-level getter and directly passed to the Python-level setter. In Qahirah, I use a 2-tuple, the first element of which specifies the alternating on/off dash lengths (itself a tuple), and the second of which is the starting offset. All conversion between this format and the actual format that Cairo expects happens within the Python wrapper routines, without the caller having to worry about it. Sometimes the opportunities to add value come from other available Python libraries. Cairo only deals with colours as RGB components, with an optional alpha. But Python provides this handy colorsys module <https://docs.python.org/3/library/colorsys.html> as standard, which provides conversions to/from a number of non-RGB colour spaces; why not take advantage of it? So I defined a “Colour” type, and all wrappers to Cairo calls that set or get colours take or return this object instead of separate RGB or RGBA components. A Colour can be constructed from components in any of the supported colour spaces, and you can query its components in any of these colour spaces. But more than that, you can also perform useful manipulations on Colours, like adjusting the values of components to produce new, but related, colours. This all goes way beyond the functionality of Cairo itself, but I felt it was useful, even essential, to have in a worthwhile graphics API. Python has a remarkably simple and elegant technique for implementing custom overloading of its standard operators. C, of course, has no such thing. So APIs designed for C have to provide function calls instead, such as for Cairo’s matrix operations <https://www.cairographics.org/manual/cairo-cairo-matrix-t.html>. It was easy enough for me to define a “Matrix” class, with the “*” operator (“@” as well in Python 3.5 or later) to do Matrix multiplication. Instead of updating a Matrix object in place (procedural programming), I prefer a more functional approach, where each operation creates a new Matrix object as its result, leaving its operands unchanged. This lets you write expressions which are closer to the underlying mathematics. I also defined a “Vector” class, representing both X and Y coordinates in a single value. Cairo itself requires you to pass (or get back) separate X and Y coordinate values; but again, I wrap all these calls so that you pass and return Vectors. It is extremely common to do calculations on corresponding X- and Y-coordinates that are absolutely identical, except for the coordinate-specific components; Qahirah lets you write these calculations just once instead of twice, operating directly on Vectors. In short: ctypes is cool. Python is cool. Python + ctypes = OMG wow cool. Yeah. :) -- https://mail.python.org/mailman/listinfo/python-list