C stable api PY_SSIZE_T_CLEAN error

I maintain an optional reportlab c extension rl-accel · PyPI

It has been working fine in Python 3.7 - 3.12 when compiled as a stable api 3.7 extension.

Problems were observed using this in 3.13 and 3.14 and I was advised that changes had made some commen macros behave differently. Compiling with a later python would improve things although speed might be reduced.

I now observe the following error when using a Python 3.14/3.13 compiled extension with python <=3.12

File “/home/robin/devel/reportlab/reportlab/pdfbase/ttfonts.py”, line 384, in makeStream
checksum = calcChecksum(data)
^^^^^^^^^^^^^^^^^^
SystemError: PY_SSIZE_T_CLEAN macro must be defined for ‘#’ formats

I thing the error comes from this function

static PyObject *ttfonts_calcChecksum(PyObject *module, PyObject* args)
{
	unsigned char	*data;
	Py_ssize_t		dataLen;
	unsigned long	Sum = 0L;
	unsigned char	*EndPtr;
	unsigned long n;
	int leftover;


	if (!PyArg_ParseTuple(args, "s#:calcChecksum", &data, &dataLen)) return NULL;

The source code contains this which does define PY_SSIZE_T_CLEAN

#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include <stdlib.h>
#include <math.h>

So I don’t know how to fix.

How I can get a module thet covers 3.9 - 3.14?

It might help if you can create a stand-alone project that triggers the issue or point to a branch in your project’s VCS someone could look at. I don’t immediately see anything wrong in the code you shared above.

I will try ripping out the checksum code into a small project and see if that fails across 3.13<->3.12.

A quick inspection of the 3.13 code indicates no usage of that define.

The rl-accel package is here branch checksum-issue

rl-accel: Summary mirror is GitHub - MrBitBucket/rl-accel-mirror

There is a runner script in the tests directory; I use two pythons win the script

PYTHONS=(${EARLYPYTHON:-~/bin/python312} ${LATEPYTHON:-~/bin/python313})

you can mess with env vars EARLYPYTHON & LATEPYTHON to set those.

When run it downloads from the git mirror and runs a test script. for me things look like this

extension built with 312 running tests/testcs.py with python312
_rl_accel.abi3.so-->build/lib.linux-x86_64-cpython-312/_rl_accel.abi3.so
3.12.9 (main, Feb  5 2025, 09:12:22) [GCC 14.2.1 20240910]
#############################################################
calcChecksum(b"Hello cruel world!")=142323306

#############################################################
extension built with 312 running tests/testcs.py with python313
_rl_accel.abi3.so-->build/lib.linux-x86_64-cpython-312/_rl_accel.abi3.so
3.13.2 (main, Feb  5 2025, 09:44:21) [GCC 14.2.1 20240910]
#############################################################
calcChecksum(b"Hello cruel world!")=142323306

#############################################################
extension built with 313 running tests/testcs.py with python312
_rl_accel.abi3.so-->build/lib.linux-x86_64-cpython-313/_rl_accel.abi3.so
3.12.9 (main, Feb  5 2025, 09:12:22) [GCC 14.2.1 20240910]
#############################################################
Traceback (most recent call last):
  File "/home/robin/devel/reportlab/REPOS/rl-accel/tests/tmp/rl-accel-mirror/tests/testcs.py", line 4, in <module>
    print(f'{calcChecksum(b"Hello cruel world!")=}')
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SystemError: PY_SSIZE_T_CLEAN macro must be defined for '#' formats

In the older versions, it looks like PY_SSIZE_T_CLEAN defined PyArg_ParseTuple as _PyArg_ParseTuple_SizeT.

_PyArg_ParseTuple_SizeT no longer appears in the headers, but is part of the stable ABI so continues to be part of the Python library.

Therefore what you want to do is use _PyArg_ParseTuple_SizeT instead (on all Python versions). Just copy the declaration of it:

PyAPI_FUNC(int)
_PyArg_ParseTuple_SizeT(PyObject *args, const char *format, ...);

to assure the C compiler that it really does exist, somewhere, and use that instead of PyArg_ParseTuple.

Thanks, I’ll give that a try.

Thanks for the assist. Your fix does work, but it seems a bit dangerous to rely on something which is not exposed or even mentioned in the C ABI documentation.