Skip to content

gh-104922: Make PY_SSIZE_T_CLEAN not mandatory again #105051

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions Doc/c-api/arg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@ unit; the entry in (round) parentheses is the Python object type that matches
the format unit; and the entry in [square] brackets is the type of the C
variable(s) whose address should be passed.

.. _arg-parsing-string-and-buffers:

Strings and buffers
-------------------

.. note::

On Python 3.12 and older, the macro :c:macro:`!PY_SSIZE_T_CLEAN` must be
defined before including :file:`Python.h` to use all ``#`` variants of
formats (``s#``, ``y#``, etc.) explained below.
This is not necessary on Python 3.13 and later.

These formats allow accessing an object as a contiguous chunk of memory.
You don't have to provide raw storage for the returned unicode or bytes
area.
Expand Down Expand Up @@ -68,15 +77,6 @@ There are three ways strings and buffers can be converted to C:
whether the input object is immutable (e.g. whether it would honor a request
for a writable buffer, or whether another thread can mutate the data).

.. note::

For all ``#`` variants of formats (``s#``, ``y#``, etc.), the macro
:c:macro:`PY_SSIZE_T_CLEAN` must be defined before including
:file:`Python.h`. On Python 3.9 and older, the type of the length argument
is :c:type:`Py_ssize_t` if the :c:macro:`PY_SSIZE_T_CLEAN` macro is defined,
or int otherwise.


``s`` (:class:`str`) [const char \*]
Convert a Unicode object to a C pointer to a character string.
A pointer to an existing string is stored in the character pointer
Expand Down
6 changes: 3 additions & 3 deletions Doc/extending/extending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ the module and a copyright notice if you like).
headers are included.

It is recommended to always define ``PY_SSIZE_T_CLEAN`` before including
``Python.h``. See :ref:`parsetuple` for a description of this macro.
``Python.h``. See :ref:`arg-parsing-string-and-buffers` for a description of this macro.

All user-visible symbols defined by :file:`Python.h` have a prefix of ``Py`` or
``PY``, except those defined in standard header files. For convenience, and
Expand Down Expand Up @@ -649,7 +649,7 @@ Note that any Python object references which are provided to the caller are

Some example calls::

#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
#define PY_SSIZE_T_CLEAN
#include <Python.h>

::
Expand Down Expand Up @@ -745,7 +745,7 @@ it returns false and raises an appropriate exception.
Here is an example module which uses keywords, based on an example by Geoff
Philbrick (philbrick@hks.com)::

#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
#define PY_SSIZE_T_CLEAN
#include <Python.h>

static PyObject *
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,13 @@ C API Changes
New Features
------------

* You no longer have to define the ``PY_SSIZE_T_CLEAN`` macro before including
:file:`Python.h` when using ``#`` formats in
:ref:`format codes <arg-parsing-string-and-buffers>`.
APIs accepting the format codes always use ``Py_ssize_t`` for ``#`` formats.
(Contributed by Inada Naoki in :gh:`104922`.)


Porting to Python 3.13
----------------------

Expand Down
15 changes: 0 additions & 15 deletions Include/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,6 @@ extern "C" {
This function always succeeds. */


#ifdef PY_SSIZE_T_CLEAN
# define PyObject_CallFunction _PyObject_CallFunction_SizeT
# define PyObject_CallMethod _PyObject_CallMethod_SizeT
#endif


#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000
/* Call a callable Python object without any arguments */
PyAPI_FUNC(PyObject *) PyObject_CallNoArgs(PyObject *func);
Expand Down Expand Up @@ -195,15 +189,6 @@ PyAPI_FUNC(PyObject *) PyObject_CallMethod(PyObject *obj,
const char *name,
const char *format, ...);

PyAPI_FUNC(PyObject *) _PyObject_CallFunction_SizeT(PyObject *callable,
const char *format,
...);

PyAPI_FUNC(PyObject *) _PyObject_CallMethod_SizeT(PyObject *obj,
const char *name,
const char *format,
...);

/* Call a callable Python object 'callable' with a variable number of C
arguments. The C arguments are provided as PyObject* values, terminated
by a NULL.
Expand Down
9 changes: 0 additions & 9 deletions Include/cpython/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@

/* === Object Protocol ================================================== */

#ifdef PY_SSIZE_T_CLEAN
# define _PyObject_CallMethodId _PyObject_CallMethodId_SizeT
#endif

/* Convert keyword arguments from the FASTCALL (stack: C array, kwnames: tuple)
format to a Python dictionary ("kwargs" dict).

Expand Down Expand Up @@ -113,11 +109,6 @@ PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,
_Py_Identifier *name,
const char *format, ...);

PyAPI_FUNC(PyObject *) _PyObject_CallMethodId_SizeT(PyObject *obj,
_Py_Identifier *name,
const char *format,
...);

PyAPI_FUNC(PyObject *) _PyObject_CallMethodIdObjArgs(
PyObject *obj,
_Py_Identifier *name,
Expand Down
21 changes: 0 additions & 21 deletions Include/cpython/modsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@
# error "this header file must not be included directly"
#endif

/* If PY_SSIZE_T_CLEAN is defined, each functions treats #-specifier
to mean Py_ssize_t */
#ifdef PY_SSIZE_T_CLEAN
#define _Py_VaBuildStack _Py_VaBuildStack_SizeT
#else
PyAPI_FUNC(PyObject *) _Py_VaBuildValue_SizeT(const char *, va_list);
PyAPI_FUNC(PyObject **) _Py_VaBuildStack_SizeT(
PyObject **small_stack,
Py_ssize_t small_stack_len,
const char *format,
va_list va,
Py_ssize_t *p_nargs);
#endif

PyAPI_FUNC(int) _PyArg_UnpackStack(
PyObject *const *args,
Py_ssize_t nargs,
Expand Down Expand Up @@ -63,13 +49,6 @@ typedef struct _PyArg_Parser {
struct _PyArg_Parser *next;
} _PyArg_Parser;

#ifdef PY_SSIZE_T_CLEAN
#define _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT
#define _PyArg_ParseStack _PyArg_ParseStack_SizeT
#define _PyArg_ParseStackAndKeywords _PyArg_ParseStackAndKeywords_SizeT
#define _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT
#endif

PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,
struct _PyArg_Parser *, ...);
PyAPI_FUNC(int) _PyArg_ParseStack(
Expand Down
23 changes: 3 additions & 20 deletions Include/modsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,17 @@ extern "C" {

#include <stdarg.h> // va_list

/* If PY_SSIZE_T_CLEAN is defined, each functions treats #-specifier
to mean Py_ssize_t */
#ifdef PY_SSIZE_T_CLEAN
#define PyArg_Parse _PyArg_Parse_SizeT
#define PyArg_ParseTuple _PyArg_ParseTuple_SizeT
#define PyArg_ParseTupleAndKeywords _PyArg_ParseTupleAndKeywords_SizeT
#define PyArg_VaParse _PyArg_VaParse_SizeT
#define PyArg_VaParseTupleAndKeywords _PyArg_VaParseTupleAndKeywords_SizeT
#define Py_BuildValue _Py_BuildValue_SizeT
#define Py_VaBuildValue _Py_VaBuildValue_SizeT
#endif

/* Due to a glitch in 3.2, the _SizeT versions weren't exported from the DLL. */
#if !defined(PY_SSIZE_T_CLEAN) || !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...);
PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...);
PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
const char *, char **, ...);
const char *, char **, ...);
PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list);
PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *,
const char *, char **, va_list);
#endif
const char *, char **, va_list);

PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *);
PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...);
PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);


PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list);

// Add an attribute with name 'name' and value 'obj' to the module 'mod.
Expand Down
17 changes: 0 additions & 17 deletions Lib/test/test_capi/test_getargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -901,23 +901,6 @@ def test_s_hash(self):
self.assertRaises(TypeError, getargs_s_hash, memoryview(b'memoryview'))
self.assertRaises(TypeError, getargs_s_hash, None)

def test_s_hash_int(self):
# "s#" without PY_SSIZE_T_CLEAN defined.
from _testcapi import getargs_s_hash_int
from _testcapi import getargs_s_hash_int2
buf = bytearray([1, 2])
self.assertRaises(SystemError, getargs_s_hash_int, buf, "abc")
self.assertRaises(SystemError, getargs_s_hash_int, buf, x=42)
self.assertRaises(SystemError, getargs_s_hash_int, buf, x="abc")
self.assertRaises(SystemError, getargs_s_hash_int2, buf, ("abc",))
self.assertRaises(SystemError, getargs_s_hash_int2, buf, x=42)
self.assertRaises(SystemError, getargs_s_hash_int2, buf, x="abc")
buf.append(3) # still mutable -- not locked by a buffer export
# getargs_s_hash_int(buf) may not raise SystemError because skipitem()
# is not called. But it is an implementation detail.
# getargs_s_hash_int(buf)
# getargs_s_hash_int2(buf)

def test_z(self):
from _testcapi import getargs_z
self.assertEqual(getargs_z('abc\xe9'), b'abc\xc3\xa9')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``PY_SSIZE_T_CLEAN`` is no longer required to use ``'#'`` formats in APIs
like :c:func:`PyArg_ParseTuple` and :c:func:`Py_BuildValue`. They uses
``Py_ssize_t`` for ``'#'`` regardless ``PY_SSIZE_T_CLEAN``.
40 changes: 0 additions & 40 deletions Modules/_testcapi/getargs.c
Original file line number Diff line number Diff line change
Expand Up @@ -816,44 +816,6 @@ test_s_code(PyObject *self, PyObject *Py_UNUSED(ignored))
Py_RETURN_NONE;
}

#undef PyArg_ParseTupleAndKeywords
PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
const char *, char **, ...);

static PyObject *
getargs_s_hash_int(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *keywords[] = {"", "", "x", NULL};
Py_buffer buf = {NULL};
const char *s;
int len;
int i = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "w*|s#i", keywords,
&buf, &s, &len, &i))
{
return NULL;
}
PyBuffer_Release(&buf);
Py_RETURN_NONE;
}

static PyObject *
getargs_s_hash_int2(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *keywords[] = {"", "", "x", NULL};
Py_buffer buf = {NULL};
const char *s;
int len;
int i = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "w*|(s#)i", keywords,
&buf, &s, &len, &i))
{
return NULL;
}
PyBuffer_Release(&buf);
Py_RETURN_NONE;
}

static PyObject *
gh_99240_clear_args(PyObject *self, PyObject *args)
{
Expand Down Expand Up @@ -906,8 +868,6 @@ static PyMethodDef test_methods[] = {
{"getargs_positional_only_and_keywords", _PyCFunction_CAST(getargs_positional_only_and_keywords), METH_VARARGS|METH_KEYWORDS},
{"getargs_s", getargs_s, METH_VARARGS},
{"getargs_s_hash", getargs_s_hash, METH_VARARGS},
{"getargs_s_hash_int", _PyCFunction_CAST(getargs_s_hash_int), METH_VARARGS|METH_KEYWORDS},
{"getargs_s_hash_int2", _PyCFunction_CAST(getargs_s_hash_int2), METH_VARARGS|METH_KEYWORDS},
{"getargs_s_star", getargs_s_star, METH_VARARGS},
{"getargs_tuple", getargs_tuple, METH_VARARGS},
{"getargs_u", getargs_u, METH_VARARGS},
Expand Down
47 changes: 0 additions & 47 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3266,8 +3266,6 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
}


static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);

static PyMethodDef TestMethods[] = {
{"set_errno", set_errno, METH_VARARGS},
{"test_config", test_config, METH_NOARGS},
Expand Down Expand Up @@ -3297,7 +3295,6 @@ static PyMethodDef TestMethods[] = {
{"getbuffer_with_null_view", getbuffer_with_null_view, METH_O},
{"PyBuffer_SizeFromFormat", test_PyBuffer_SizeFromFormat, METH_VARARGS},
{"test_buildvalue_N", test_buildvalue_N, METH_NOARGS},
{"test_buildvalue_issue38913", test_buildvalue_issue38913, METH_NOARGS},
{"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS},
{"test_get_type_name", test_get_type_name, METH_NOARGS},
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
Expand Down Expand Up @@ -4067,47 +4064,3 @@ PyInit__testcapi(void)
PyState_AddModule(m, &_testcapimodule);
return m;
}

/* Test the C API exposed when PY_SSIZE_T_CLEAN is not defined */

#undef Py_BuildValue
PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);

static PyObject *
test_buildvalue_issue38913(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *res;
const char str[] = "string";
const Py_UNICODE unicode[] = L"unicode";
assert(!PyErr_Occurred());

res = Py_BuildValue("(s#O)", str, 1, Py_None);
assert(res == NULL);
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
return NULL;
}
PyErr_Clear();

res = Py_BuildValue("(z#O)", str, 1, Py_None);
assert(res == NULL);
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
return NULL;
}
PyErr_Clear();

res = Py_BuildValue("(y#O)", str, 1, Py_None);
assert(res == NULL);
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
return NULL;
}
PyErr_Clear();

res = Py_BuildValue("(u#O)", unicode, 1, Py_None);
assert(res == NULL);
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
return NULL;
}
PyErr_Clear();

Py_RETURN_NONE;
}
Loading