Skip to content

Commit adccff3

Browse files
authored
gh-104922: Make PY_SSIZE_T_CLEAN not mandatory again (#105051)
1 parent f90d3f6 commit adccff3

File tree

15 files changed

+106
-398
lines changed

15 files changed

+106
-398
lines changed

Doc/c-api/arg.rst

+9-9
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,18 @@ unit; the entry in (round) parentheses is the Python object type that matches
2727
the format unit; and the entry in [square] brackets is the type of the C
2828
variable(s) whose address should be passed.
2929

30+
.. _arg-parsing-string-and-buffers:
31+
3032
Strings and buffers
3133
-------------------
3234

35+
.. note::
36+
37+
On Python 3.12 and older, the macro :c:macro:`!PY_SSIZE_T_CLEAN` must be
38+
defined before including :file:`Python.h` to use all ``#`` variants of
39+
formats (``s#``, ``y#``, etc.) explained below.
40+
This is not necessary on Python 3.13 and later.
41+
3342
These formats allow accessing an object as a contiguous chunk of memory.
3443
You don't have to provide raw storage for the returned unicode or bytes
3544
area.
@@ -68,15 +77,6 @@ There are three ways strings and buffers can be converted to C:
6877
whether the input object is immutable (e.g. whether it would honor a request
6978
for a writable buffer, or whether another thread can mutate the data).
7079

71-
.. note::
72-
73-
For all ``#`` variants of formats (``s#``, ``y#``, etc.), the macro
74-
:c:macro:`PY_SSIZE_T_CLEAN` must be defined before including
75-
:file:`Python.h`. On Python 3.9 and older, the type of the length argument
76-
is :c:type:`Py_ssize_t` if the :c:macro:`PY_SSIZE_T_CLEAN` macro is defined,
77-
or int otherwise.
78-
79-
8080
``s`` (:class:`str`) [const char \*]
8181
Convert a Unicode object to a C pointer to a character string.
8282
A pointer to an existing string is stored in the character pointer

Doc/extending/extending.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ the module and a copyright notice if you like).
7070
headers are included.
7171

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

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

650650
Some example calls::
651651

652-
#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
652+
#define PY_SSIZE_T_CLEAN
653653
#include <Python.h>
654654

655655
::
@@ -745,7 +745,7 @@ it returns false and raises an appropriate exception.
745745
Here is an example module which uses keywords, based on an example by Geoff
746746
Philbrick ([email protected])::
747747

748-
#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
748+
#define PY_SSIZE_T_CLEAN
749749
#include <Python.h>
750750

751751
static PyObject *

Doc/whatsnew/3.13.rst

+7
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,13 @@ C API Changes
292292
New Features
293293
------------
294294

295+
* You no longer have to define the ``PY_SSIZE_T_CLEAN`` macro before including
296+
:file:`Python.h` when using ``#`` formats in
297+
:ref:`format codes <arg-parsing-string-and-buffers>`.
298+
APIs accepting the format codes always use ``Py_ssize_t`` for ``#`` formats.
299+
(Contributed by Inada Naoki in :gh:`104922`.)
300+
301+
295302
Porting to Python 3.13
296303
----------------------
297304

Include/abstract.h

-15
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,6 @@ extern "C" {
135135
This function always succeeds. */
136136

137137

138-
#ifdef PY_SSIZE_T_CLEAN
139-
# define PyObject_CallFunction _PyObject_CallFunction_SizeT
140-
# define PyObject_CallMethod _PyObject_CallMethod_SizeT
141-
#endif
142-
143-
144138
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000
145139
/* Call a callable Python object without any arguments */
146140
PyAPI_FUNC(PyObject *) PyObject_CallNoArgs(PyObject *func);
@@ -195,15 +189,6 @@ PyAPI_FUNC(PyObject *) PyObject_CallMethod(PyObject *obj,
195189
const char *name,
196190
const char *format, ...);
197191

198-
PyAPI_FUNC(PyObject *) _PyObject_CallFunction_SizeT(PyObject *callable,
199-
const char *format,
200-
...);
201-
202-
PyAPI_FUNC(PyObject *) _PyObject_CallMethod_SizeT(PyObject *obj,
203-
const char *name,
204-
const char *format,
205-
...);
206-
207192
/* Call a callable Python object 'callable' with a variable number of C
208193
arguments. The C arguments are provided as PyObject* values, terminated
209194
by a NULL.

Include/cpython/abstract.h

-9
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44

55
/* === Object Protocol ================================================== */
66

7-
#ifdef PY_SSIZE_T_CLEAN
8-
# define _PyObject_CallMethodId _PyObject_CallMethodId_SizeT
9-
#endif
10-
117
/* Convert keyword arguments from the FASTCALL (stack: C array, kwnames: tuple)
128
format to a Python dictionary ("kwargs" dict).
139
@@ -113,11 +109,6 @@ PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,
113109
_Py_Identifier *name,
114110
const char *format, ...);
115111

116-
PyAPI_FUNC(PyObject *) _PyObject_CallMethodId_SizeT(PyObject *obj,
117-
_Py_Identifier *name,
118-
const char *format,
119-
...);
120-
121112
PyAPI_FUNC(PyObject *) _PyObject_CallMethodIdObjArgs(
122113
PyObject *obj,
123114
_Py_Identifier *name,

Include/cpython/modsupport.h

-21
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,6 @@
22
# error "this header file must not be included directly"
33
#endif
44

5-
/* If PY_SSIZE_T_CLEAN is defined, each functions treats #-specifier
6-
to mean Py_ssize_t */
7-
#ifdef PY_SSIZE_T_CLEAN
8-
#define _Py_VaBuildStack _Py_VaBuildStack_SizeT
9-
#else
10-
PyAPI_FUNC(PyObject *) _Py_VaBuildValue_SizeT(const char *, va_list);
11-
PyAPI_FUNC(PyObject **) _Py_VaBuildStack_SizeT(
12-
PyObject **small_stack,
13-
Py_ssize_t small_stack_len,
14-
const char *format,
15-
va_list va,
16-
Py_ssize_t *p_nargs);
17-
#endif
18-
195
PyAPI_FUNC(int) _PyArg_UnpackStack(
206
PyObject *const *args,
217
Py_ssize_t nargs,
@@ -63,13 +49,6 @@ typedef struct _PyArg_Parser {
6349
struct _PyArg_Parser *next;
6450
} _PyArg_Parser;
6551

66-
#ifdef PY_SSIZE_T_CLEAN
67-
#define _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT
68-
#define _PyArg_ParseStack _PyArg_ParseStack_SizeT
69-
#define _PyArg_ParseStackAndKeywords _PyArg_ParseStackAndKeywords_SizeT
70-
#define _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT
71-
#endif
72-
7352
PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,
7453
struct _PyArg_Parser *, ...);
7554
PyAPI_FUNC(int) _PyArg_ParseStack(

Include/modsupport.h

+3-20
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,17 @@ extern "C" {
99

1010
#include <stdarg.h> // va_list
1111

12-
/* If PY_SSIZE_T_CLEAN is defined, each functions treats #-specifier
13-
to mean Py_ssize_t */
14-
#ifdef PY_SSIZE_T_CLEAN
15-
#define PyArg_Parse _PyArg_Parse_SizeT
16-
#define PyArg_ParseTuple _PyArg_ParseTuple_SizeT
17-
#define PyArg_ParseTupleAndKeywords _PyArg_ParseTupleAndKeywords_SizeT
18-
#define PyArg_VaParse _PyArg_VaParse_SizeT
19-
#define PyArg_VaParseTupleAndKeywords _PyArg_VaParseTupleAndKeywords_SizeT
20-
#define Py_BuildValue _Py_BuildValue_SizeT
21-
#define Py_VaBuildValue _Py_VaBuildValue_SizeT
22-
#endif
23-
24-
/* Due to a glitch in 3.2, the _SizeT versions weren't exported from the DLL. */
25-
#if !defined(PY_SSIZE_T_CLEAN) || !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
2612
PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...);
2713
PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...);
2814
PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
29-
const char *, char **, ...);
15+
const char *, char **, ...);
3016
PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list);
3117
PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *,
32-
const char *, char **, va_list);
33-
#endif
18+
const char *, char **, va_list);
19+
3420
PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *);
3521
PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...);
3622
PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
37-
PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);
38-
39-
4023
PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list);
4124

4225
// Add an attribute with name 'name' and value 'obj' to the module 'mod.

Lib/test/test_capi/test_getargs.py

-17
Original file line numberDiff line numberDiff line change
@@ -901,23 +901,6 @@ def test_s_hash(self):
901901
self.assertRaises(TypeError, getargs_s_hash, memoryview(b'memoryview'))
902902
self.assertRaises(TypeError, getargs_s_hash, None)
903903

904-
def test_s_hash_int(self):
905-
# "s#" without PY_SSIZE_T_CLEAN defined.
906-
from _testcapi import getargs_s_hash_int
907-
from _testcapi import getargs_s_hash_int2
908-
buf = bytearray([1, 2])
909-
self.assertRaises(SystemError, getargs_s_hash_int, buf, "abc")
910-
self.assertRaises(SystemError, getargs_s_hash_int, buf, x=42)
911-
self.assertRaises(SystemError, getargs_s_hash_int, buf, x="abc")
912-
self.assertRaises(SystemError, getargs_s_hash_int2, buf, ("abc",))
913-
self.assertRaises(SystemError, getargs_s_hash_int2, buf, x=42)
914-
self.assertRaises(SystemError, getargs_s_hash_int2, buf, x="abc")
915-
buf.append(3) # still mutable -- not locked by a buffer export
916-
# getargs_s_hash_int(buf) may not raise SystemError because skipitem()
917-
# is not called. But it is an implementation detail.
918-
# getargs_s_hash_int(buf)
919-
# getargs_s_hash_int2(buf)
920-
921904
def test_z(self):
922905
from _testcapi import getargs_z
923906
self.assertEqual(getargs_z('abc\xe9'), b'abc\xc3\xa9')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``PY_SSIZE_T_CLEAN`` is no longer required to use ``'#'`` formats in APIs
2+
like :c:func:`PyArg_ParseTuple` and :c:func:`Py_BuildValue`. They uses
3+
``Py_ssize_t`` for ``'#'`` regardless ``PY_SSIZE_T_CLEAN``.

Modules/_testcapi/getargs.c

-40
Original file line numberDiff line numberDiff line change
@@ -816,44 +816,6 @@ test_s_code(PyObject *self, PyObject *Py_UNUSED(ignored))
816816
Py_RETURN_NONE;
817817
}
818818

819-
#undef PyArg_ParseTupleAndKeywords
820-
PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
821-
const char *, char **, ...);
822-
823-
static PyObject *
824-
getargs_s_hash_int(PyObject *self, PyObject *args, PyObject *kwargs)
825-
{
826-
static char *keywords[] = {"", "", "x", NULL};
827-
Py_buffer buf = {NULL};
828-
const char *s;
829-
int len;
830-
int i = 0;
831-
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "w*|s#i", keywords,
832-
&buf, &s, &len, &i))
833-
{
834-
return NULL;
835-
}
836-
PyBuffer_Release(&buf);
837-
Py_RETURN_NONE;
838-
}
839-
840-
static PyObject *
841-
getargs_s_hash_int2(PyObject *self, PyObject *args, PyObject *kwargs)
842-
{
843-
static char *keywords[] = {"", "", "x", NULL};
844-
Py_buffer buf = {NULL};
845-
const char *s;
846-
int len;
847-
int i = 0;
848-
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "w*|(s#)i", keywords,
849-
&buf, &s, &len, &i))
850-
{
851-
return NULL;
852-
}
853-
PyBuffer_Release(&buf);
854-
Py_RETURN_NONE;
855-
}
856-
857819
static PyObject *
858820
gh_99240_clear_args(PyObject *self, PyObject *args)
859821
{
@@ -906,8 +868,6 @@ static PyMethodDef test_methods[] = {
906868
{"getargs_positional_only_and_keywords", _PyCFunction_CAST(getargs_positional_only_and_keywords), METH_VARARGS|METH_KEYWORDS},
907869
{"getargs_s", getargs_s, METH_VARARGS},
908870
{"getargs_s_hash", getargs_s_hash, METH_VARARGS},
909-
{"getargs_s_hash_int", _PyCFunction_CAST(getargs_s_hash_int), METH_VARARGS|METH_KEYWORDS},
910-
{"getargs_s_hash_int2", _PyCFunction_CAST(getargs_s_hash_int2), METH_VARARGS|METH_KEYWORDS},
911871
{"getargs_s_star", getargs_s_star, METH_VARARGS},
912872
{"getargs_tuple", getargs_tuple, METH_VARARGS},
913873
{"getargs_u", getargs_u, METH_VARARGS},

Modules/_testcapimodule.c

-47
Original file line numberDiff line numberDiff line change
@@ -3266,8 +3266,6 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
32663266
}
32673267

32683268

3269-
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
3270-
32713269
static PyMethodDef TestMethods[] = {
32723270
{"set_errno", set_errno, METH_VARARGS},
32733271
{"test_config", test_config, METH_NOARGS},
@@ -3297,7 +3295,6 @@ static PyMethodDef TestMethods[] = {
32973295
{"getbuffer_with_null_view", getbuffer_with_null_view, METH_O},
32983296
{"PyBuffer_SizeFromFormat", test_PyBuffer_SizeFromFormat, METH_VARARGS},
32993297
{"test_buildvalue_N", test_buildvalue_N, METH_NOARGS},
3300-
{"test_buildvalue_issue38913", test_buildvalue_issue38913, METH_NOARGS},
33013298
{"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS},
33023299
{"test_get_type_name", test_get_type_name, METH_NOARGS},
33033300
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
@@ -4067,47 +4064,3 @@ PyInit__testcapi(void)
40674064
PyState_AddModule(m, &_testcapimodule);
40684065
return m;
40694066
}
4070-
4071-
/* Test the C API exposed when PY_SSIZE_T_CLEAN is not defined */
4072-
4073-
#undef Py_BuildValue
4074-
PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
4075-
4076-
static PyObject *
4077-
test_buildvalue_issue38913(PyObject *self, PyObject *Py_UNUSED(ignored))
4078-
{
4079-
PyObject *res;
4080-
const char str[] = "string";
4081-
const Py_UNICODE unicode[] = L"unicode";
4082-
assert(!PyErr_Occurred());
4083-
4084-
res = Py_BuildValue("(s#O)", str, 1, Py_None);
4085-
assert(res == NULL);
4086-
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
4087-
return NULL;
4088-
}
4089-
PyErr_Clear();
4090-
4091-
res = Py_BuildValue("(z#O)", str, 1, Py_None);
4092-
assert(res == NULL);
4093-
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
4094-
return NULL;
4095-
}
4096-
PyErr_Clear();
4097-
4098-
res = Py_BuildValue("(y#O)", str, 1, Py_None);
4099-
assert(res == NULL);
4100-
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
4101-
return NULL;
4102-
}
4103-
PyErr_Clear();
4104-
4105-
res = Py_BuildValue("(u#O)", unicode, 1, Py_None);
4106-
assert(res == NULL);
4107-
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
4108-
return NULL;
4109-
}
4110-
PyErr_Clear();
4111-
4112-
Py_RETURN_NONE;
4113-
}

0 commit comments

Comments
 (0)