Skip to content

Commit 6f93ff6

Browse files
committed
WIP: pythongh-106593: PyResource API
* Add PyUnicode_AsUTF8Resource() * Add PyBytes_AsStringResource() * Add PySequence_AsObjectArray() * Add Include/pyresource.h * Add PyResource_Release() to the stable ABI * compute_abstract_methods(): Replace PySequence_Fast() with PySequence_AsObjectArray()
1 parent 9384665 commit 6f93ff6

17 files changed

+172
-16
lines changed

Doc/data/stable_abi.dat

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/Python.h

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "pymacro.h"
4040
#include "pymath.h"
4141
#include "pymem.h"
42+
#include "pyresource.h"
4243
#include "pytypedefs.h"
4344
#include "pybuffer.h"
4445
#include "object.h"

Include/abstract.h

+6
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,12 @@ PyAPI_FUNC(PyObject *) PySequence_List(PyObject *o);
676676
TypeError exception with 'm' as the message text. */
677677
PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m);
678678

679+
PyAPI_FUNC(int) PySequence_AsObjectArray(
680+
PyObject *,
681+
PyResource *res,
682+
PyObject ***parray,
683+
Py_ssize_t *psize);
684+
679685
/* Return the size of the sequence 'o', assuming that 'o' was returned by
680686
PySequence_Fast and is not NULL. */
681687
#define PySequence_Fast_GET_SIZE(o) \

Include/bytesobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...)
4040
Py_GCC_ATTRIBUTE((format(printf, 1, 2)));
4141
PyAPI_FUNC(Py_ssize_t) PyBytes_Size(PyObject *);
4242
PyAPI_FUNC(char *) PyBytes_AsString(PyObject *);
43+
PyAPI_FUNC(char *) PyBytes_AsStringResource(PyObject *, PyResource *res);
4344
PyAPI_FUNC(PyObject *) PyBytes_Repr(PyObject *, int);
4445
PyAPI_FUNC(void) PyBytes_Concat(PyObject **, PyObject *);
4546
PyAPI_FUNC(void) PyBytes_ConcatAndDel(PyObject **, PyObject *);

Include/cpython/unicodeobject.h

+4
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,10 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromKindAndData(
455455

456456
PyAPI_FUNC(const char *) PyUnicode_AsUTF8(PyObject *unicode);
457457

458+
PyAPI_FUNC(const char *) PyUnicode_AsUTF8(PyObject *unicode);
459+
460+
PyAPI_FUNC(const char *) PyUnicode_AsUTF8Resource(PyObject *unicode, PyResource *res);
461+
458462
#define _PyUnicode_AsString PyUnicode_AsUTF8
459463

460464
/* === Characters Type APIs =============================================== */

Include/pyresource.h

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef Py_RESOURCE_H
2+
#define Py_RESOURCE_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
typedef struct {
8+
void (*release_func) (void *data);
9+
void *data;
10+
} PyResource;
11+
12+
PyAPI_FUNC(void) PyResource_Release(PyResource *res);
13+
14+
#ifdef Py_BUILD_CORE
15+
PyAPI_FUNC(void) _PyResource_DECREF(void *data);
16+
#endif
17+
18+
#ifdef __cplusplus
19+
}
20+
#endif
21+
#endif // !Py_RESOURCE_H

Lib/test/test_stable_abi_ctypes.py

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile.pre.in

+1
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,7 @@ PYTHON_HEADERS= \
16551655
$(srcdir)/Include/pymath.h \
16561656
$(srcdir)/Include/pymem.h \
16571657
$(srcdir)/Include/pyport.h \
1658+
$(srcdir)/Include/pyresource.h \
16581659
$(srcdir)/Include/pystate.h \
16591660
$(srcdir)/Include/pystats.h \
16601661
$(srcdir)/Include/pystrcmp.h \

Misc/stable_abi.toml

+2
Original file line numberDiff line numberDiff line change
@@ -2432,3 +2432,5 @@
24322432
added = '3.13'
24332433
[function.PyWeakref_GetRef]
24342434
added = '3.13'
2435+
[function.PyResource_Release]
2436+
added = '3.13'

Modules/_abc.c

+28-16
Original file line numberDiff line numberDiff line change
@@ -317,34 +317,46 @@ compute_abstract_methods(PyObject *self)
317317
}
318318
assert(PyList_Check(items));
319319
for (Py_ssize_t pos = 0; pos < PyList_GET_SIZE(items); pos++) {
320-
PyObject *it = PySequence_Fast(
321-
PyList_GET_ITEM(items, pos),
322-
"items() returned non-iterable");
323-
if (!it) {
320+
PyObject *seq = PyList_GET_ITEM(items, pos);
321+
PyResource res;
322+
PyObject **items;
323+
Py_ssize_t nitem;
324+
if (PySequence_AsObjectArray(seq, &res, &items, &nitem) < 0) {
325+
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
326+
PyErr_SetString(PyExc_TypeError, "items() returned non-iterable");
327+
}
324328
goto error;
325329
}
326-
if (PySequence_Fast_GET_SIZE(it) != 2) {
330+
if (nitem != 2) {
327331
PyErr_SetString(PyExc_TypeError,
328332
"items() returned item which size is not 2");
329-
Py_DECREF(it);
333+
PyResource_Release(&res);
330334
goto error;
331335
}
332336

333-
// borrowed
334-
PyObject *key = PySequence_Fast_GET_ITEM(it, 0);
335-
PyObject *value = PySequence_Fast_GET_ITEM(it, 1);
336337
// items or it may be cleared while accessing __abstractmethod__
337338
// So we need to keep strong reference for key
338-
Py_INCREF(key);
339+
PyObject *key = Py_NewRef(items[0]);
340+
PyObject *value = Py_NewRef(items[1]);
341+
PyResource_Release(&res);
342+
339343
int is_abstract = _PyObject_IsAbstract(value);
340-
if (is_abstract < 0 ||
341-
(is_abstract && PySet_Add(abstracts, key) < 0)) {
342-
Py_DECREF(it);
343-
Py_DECREF(key);
344-
goto error;
344+
int err;
345+
if (is_abstract < 0) {
346+
err = 1;
347+
}
348+
else if (is_abstract) {
349+
err = PySet_Add(abstracts, key) < 0;
350+
}
351+
else {
352+
err = 0;
345353
}
346354
Py_DECREF(key);
347-
Py_DECREF(it);
355+
Py_DECREF(value);
356+
357+
if (err) {
358+
goto error;
359+
}
348360
}
349361

350362
/* Stage 2: inherited abstract methods. */

Objects/abstract.c

+66
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,72 @@ PySequence_Fast(PyObject *v, const char *m)
21812181
return v;
21822182
}
21832183

2184+
static void pysequence_decref_track(void *data)
2185+
{
2186+
PyObject *obj = _PyObject_CAST(data);
2187+
_PyObject_GC_TRACK(obj);
2188+
Py_DECREF(obj);
2189+
}
2190+
2191+
int
2192+
PySequence_AsObjectArray(PyObject *v, PyResource *res,
2193+
PyObject ***parray, Py_ssize_t *psize)
2194+
{
2195+
if (v == NULL) {
2196+
null_error();
2197+
goto error;
2198+
}
2199+
2200+
if (PyTuple_CheckExact(v)) {
2201+
if (_PyObject_GC_IS_TRACKED(v)) {
2202+
// Don't track the tuple while it's being used
2203+
_PyObject_GC_UNTRACK(v);
2204+
res->release_func = pysequence_decref_track;
2205+
}
2206+
else {
2207+
res->release_func = _PyResource_DECREF;
2208+
}
2209+
res->data = Py_NewRef(v);
2210+
*parray = _PyTuple_ITEMS(v);
2211+
*psize = PyTuple_GET_SIZE(v);
2212+
return 0;
2213+
}
2214+
2215+
if (PyList_CheckExact(v)) {
2216+
// Don't track the list while it's being used
2217+
_PyObject_GC_UNTRACK(v);
2218+
res->release_func = pysequence_decref_track;
2219+
res->data = Py_NewRef(v);
2220+
*parray = _PyList_ITEMS(v);
2221+
*psize = PyList_GET_SIZE(v);
2222+
return 0;
2223+
}
2224+
2225+
PyObject *it = PyObject_GetIter(v);
2226+
if (it == NULL) {
2227+
goto error;
2228+
}
2229+
2230+
v = PySequence_List(it);
2231+
Py_DECREF(it);
2232+
if (v == NULL) {
2233+
goto error;
2234+
}
2235+
2236+
// Don't track the list while it's being used
2237+
_PyObject_GC_UNTRACK(v);
2238+
res->release_func = pysequence_decref_track;
2239+
res->data = v;
2240+
*parray = _PyList_ITEMS(v);
2241+
*psize = PyList_GET_SIZE(v);
2242+
return 0;
2243+
2244+
error:
2245+
*parray = NULL;
2246+
*psize = 0;
2247+
return -1;
2248+
}
2249+
21842250
/* Iterate over seq. Result depends on the operation:
21852251
PY_ITERSEARCH_COUNT: -1 if error, else # of times obj appears in seq.
21862252
PY_ITERSEARCH_INDEX: 0-based index of first occurrence of obj in seq;

Objects/bytesobject.c

+13
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,19 @@ PyBytes_AsString(PyObject *op)
12201220
return ((PyBytesObject *)op)->ob_sval;
12211221
}
12221222

1223+
char *
1224+
PyBytes_AsStringResource(PyObject *op, PyResource *res)
1225+
{
1226+
if (!PyBytes_Check(op)) {
1227+
PyErr_Format(PyExc_TypeError,
1228+
"expected bytes, %.200s found", Py_TYPE(op)->tp_name);
1229+
return NULL;
1230+
}
1231+
res->release_func = _PyResource_DECREF;
1232+
res->data = Py_NewRef(op);
1233+
return ((PyBytesObject *)op)->ob_sval;
1234+
}
1235+
12231236
int
12241237
PyBytes_AsStringAndSize(PyObject *obj,
12251238
char **s,

Objects/object.c

+14
Original file line numberDiff line numberDiff line change
@@ -2652,6 +2652,20 @@ int Py_IsFalse(PyObject *x)
26522652
return Py_Is(x, Py_False);
26532653
}
26542654

2655+
void _PyResource_DECREF(void *data)
2656+
{
2657+
PyObject *obj = _PyObject_CAST(data);
2658+
Py_DECREF(obj);
2659+
}
2660+
2661+
void PyResource_Release(PyResource *res)
2662+
{
2663+
if (res->release_func == 0) {
2664+
return;
2665+
}
2666+
res->release_func(res->data);
2667+
}
2668+
26552669
#ifdef __cplusplus
26562670
}
26572671
#endif

Objects/unicodeobject.c

+8
Original file line numberDiff line numberDiff line change
@@ -3806,6 +3806,14 @@ PyUnicode_AsUTF8(PyObject *unicode)
38063806
return PyUnicode_AsUTF8AndSize(unicode, NULL);
38073807
}
38083808

3809+
const char *
3810+
PyUnicode_AsUTF8Resource(PyObject *unicode, PyResource *res)
3811+
{
3812+
res->release_func = _PyResource_DECREF;
3813+
res->data = Py_NewRef(unicode);
3814+
return PyUnicode_AsUTF8AndSize(unicode, NULL);
3815+
}
3816+
38093817
/*
38103818
PyUnicode_GetSize() has been deprecated since Python 3.3
38113819
because it returned length of Py_UNICODE.

PC/python3dll.c

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PCbuild/pythoncore.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@
308308
<ClInclude Include="..\Include\pymath.h" />
309309
<ClInclude Include="..\Include\pymem.h" />
310310
<ClInclude Include="..\Include\pyport.h" />
311+
<ClInclude Include="..\Include\pyresource.h" />
311312
<ClInclude Include="..\Include\pystate.h" />
312313
<ClInclude Include="..\Include\pystats.h" />
313314
<ClInclude Include="..\Include\pystrcmp.h" />

PCbuild/pythoncore.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@
171171
<ClInclude Include="..\Include\pyport.h">
172172
<Filter>Include</Filter>
173173
</ClInclude>
174+
<ClInclude Include="..\Include\pyresource.h">
175+
<Filter>Include</Filter>
176+
</ClInclude>
174177
<ClInclude Include="..\Include\pystate.h">
175178
<Filter>Include</Filter>
176179
</ClInclude>

0 commit comments

Comments
 (0)