Python C API

It is usually easier to write and debug Python scripts than do it in C/C++, while for heavy computation tasks, C or C++ programs can run much faster. A common strategy to get the best of both worlds is writing the computation kernels in C/C++ and binding Python APIs to invoke the computation kernels in Python scripts. Python C API exists exactly for the purpose of binding Python with C/C++.

I learnt Python C API recently, and this post records my practice of creating a Python package, txt2qr, that uses C library libqrencode. Txt2QR generates the QR code (in SVG format) of a given text, and is easily integrated into a Django app.

Python C API

Python C API extends Python with C or C++. The tutorial on Python website covers:

  • Create Python modules (PyModule_Create(PyModuleDef))
  • Register module methods (PyMethodDef array in PyModuleDef)
  • Extension function interface (convert arguments from PyObject and return result as PyObject)
  • Define exception (PyErr_NewException)
  • Define object type (PyType_Ready(PyTypeObject) to initilize the type then PyModule_AddObjectRef() to add the type into a module)
  • Special object methods and attribute control (e.g., register accessors with PyGetSetDef tp_getset field in PyTypeObject, register attributes with PyMemberDef tp_members field in PyTypeObject with READONLY flags)
  • Reference count mechanism for dynamic memory management (Py_INCREF() and Py_DECREF())
  • Capsule approach for extension modules to be imported by other extension modules
  • And many more

Following the tutorial, I was able to write some “hello world” Python extension modules. I brainstormed where this technique could be used, then there comes my Txt2QR project.

Txt2QR

QR code (quick-response code) is a widely used encoding method that represents information in a two dimentional grid of black/white dots. QR code makes it convenient to pass messages to devices with camera or scanner. I occationally want to generate QR codes for URLs (used in a presentation for example). For those commercial online tools. after the free trial, a subscription fee would be charged, or I need to sign up a new account. I have been longing to create my own free-forever solution: Txt2QR is such a project that converts any given string into a QR code and display it.

I do not want to test my understanding of QR code encoding algorithm by writing a C++ program to do so, especially the error correction part. This brings me to the libqrencode library, which is a C library that encodes information into QR codes. The APIs of libqrencode are well documented. After reading, I realize the APIs are very stright-forward that the inputs could be one of the following three:

  1. a C style string (char array ending with NULL);
  2. a QRinput structure that holds not only the char pointer but other meta info (e.g., minimal version, error correction level, hint on the character set);
  3. a link list of QRinput structure;

And the output would be the QRcode structure holding QR code info in a bit mask array with version and width info. Based on which encoding function is called, the output could be micro-QR code or QR code, single QRcode structure or a linked list. The most basic version of the encoding API is QRcode_encodeString(), which encodes a string in a single QR code and it provides all the functionality my project needs. So I write a Python extension module named pyqrencode to bind QRcode_encodeString() into Python.

Within pyqrencode module, I registered:

  1. a Python type QRcode which is one-to-one mapping to the C struct QRcode, that has a bit mask array, version and width;
  2. a Python method enc() that takes a Python unicode string, feeds into QRcode_encodeString() function with some default config, and return the encoding result in a QRcode Python object;

The inital version of pyqrencode module binding to libqrencode is as simple as this. Later I added a Python type QRinput with more encoding configuration options for the improvement idea.

I want my Txt2QR Python package provides some visualization capability, rather than simply staying at the level of binding libqrencode APIs. So I also write a simple Python module file txt2qr.py, that defines a svg() method, where the bit mask array is iterated through to generate a SVG image. By the way, SVG is a very nice way for visualization, as it is essentially text-based XML document and most browsers can draw the figure described by the SVG, with the capability of scaling and interactions.

To package the pyqrencode and txt2qr modules into one Python package, I use setuptools to configure the dependencies in a setup.py file. Under the hood of the pip install . command, it executes ./setup.py develop to get the C extension module compiled, and also build the package distribution to install. So this is a pretty standard Python packaging flow. With the txt2qr Python package installed, a QR code SVG could be produced like the following:

    import txt2qr
    with open("qr.svg", "w") as fo:
        fo.write(txt2qr.svg("txt2qr is amazing"))

To serve users who do not do Python programming, I also write a web page where users submit the text to a Django backend application, then the page loads the generated QR code for the given text.

Even though I did not dig deep into the error correction part of the QR code encoding algorithm, I got to know that QR code supports multiple levels of error correction, and at the highest level the information could be recovered even many bits in the QR code were wrong. So soon after I get the web page work, an improvement idea runs into my mind: what if I generate a SVG QR code that users can click on the cells to flip (black dot to white space, or vice versa)? Then users could make a little bit of pixel art on the QR code, with the message encoded in the QR code still can be delivered after error correction. For example, the following QR code encodes “LOVE”, and I augmented it with a heart shape drawn in pixels. You should be able to get the “LOVE” message from scanning, and further edit the QR code by clicking.

Closing

The Txt2QR project is a great practice of what I learnt about Python C extension, Python packaging, SVG and web development. Additionally, the outcome is a useful tool that I would definitly need it sometime. If you are interested in accessing Txt2QR repo please let me know.

Credits