Skip to content

Commit

Permalink
WIP: pythongh-106593: PyResource API
Browse files Browse the repository at this point in the history
* Add PyUnicode_AsUTF8Resource()
* Add PyBytes_AsStringResource()
* Add PySequence_AsObjectArray()
* Add Include/pyresource.h
* Add PyResource_Close() to the stable ABI
* compute_abstract_methods(): Replace PySequence_Fast()
  with PySequence_AsObjectArray()
  • Loading branch information
vstinner committed Jul 17, 2023
1 parent 7e96370 commit 98d883b
Show file tree
Hide file tree
Showing 23 changed files with 271 additions and 26 deletions.
2 changes: 2 additions & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/Python.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "pymacro.h"
#include "pymath.h"
#include "pymem.h"
#include "pyresource.h"
#include "pytypedefs.h"
#include "pybuffer.h"
#include "object.h"
Expand Down
6 changes: 6 additions & 0 deletions Include/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,12 @@ PyAPI_FUNC(PyObject *) PySequence_List(PyObject *o);
TypeError exception with 'm' as the message text. */
PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m);

PyAPI_FUNC(int) PySequence_AsObjectArray(
PyObject *,
PyObject ***parray,
Py_ssize_t *psize,
PyResource *res);

/* Return the size of the sequence 'o', assuming that 'o' was returned by
PySequence_Fast and is not NULL. */
#define PySequence_Fast_GET_SIZE(o) \
Expand Down
3 changes: 2 additions & 1 deletion Include/bytearrayobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ PyAPI_FUNC(PyObject *) PyByteArray_FromObject(PyObject *);
PyAPI_FUNC(PyObject *) PyByteArray_Concat(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyByteArray_FromStringAndSize(const char *, Py_ssize_t);
PyAPI_FUNC(Py_ssize_t) PyByteArray_Size(PyObject *);
PyAPI_FUNC(char *) PyByteArray_AsString(PyObject *);
PyAPI_FUNC(char*) PyByteArray_AsString(PyObject *op);
PyAPI_FUNC(char*) PyByteArray_AsStringRes(PyObject *op, PyResource *res);
PyAPI_FUNC(int) PyByteArray_Resize(PyObject *, Py_ssize_t);

#ifndef Py_LIMITED_API
Expand Down
3 changes: 2 additions & 1 deletion Include/bytesobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ PyAPI_FUNC(PyObject *) PyBytes_FromFormatV(const char*, va_list)
PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...)
Py_GCC_ATTRIBUTE((format(printf, 1, 2)));
PyAPI_FUNC(Py_ssize_t) PyBytes_Size(PyObject *);
PyAPI_FUNC(char *) PyBytes_AsString(PyObject *);
PyAPI_FUNC(char*) PyBytes_AsString(PyObject *op);
PyAPI_FUNC(const char*) PyBytes_AsStringRes(PyObject *op, PyResource *res);
PyAPI_FUNC(PyObject *) PyBytes_Repr(PyObject *, int);
PyAPI_FUNC(void) PyBytes_Concat(PyObject **, PyObject *);
PyAPI_FUNC(void) PyBytes_ConcatAndDel(PyObject **, PyObject *);
Expand Down
3 changes: 2 additions & 1 deletion Include/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(void);
PyAPI_FUNC(int) Py_EnterRecursiveCall(const char *where);
PyAPI_FUNC(void) Py_LeaveRecursiveCall(void);

PyAPI_FUNC(const char *) PyEval_GetFuncName(PyObject *);
PyAPI_FUNC(const char *) PyEval_GetFuncName(PyObject *func);
PyAPI_FUNC(const char *) PyEval_GetFuncNameRes(PyObject *func, PyResource *res);
PyAPI_FUNC(const char *) PyEval_GetFuncDesc(PyObject *);

PyAPI_FUNC(PyObject *) PyEval_EvalFrame(PyFrameObject *);
Expand Down
15 changes: 12 additions & 3 deletions Include/cpython/unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromKindAndData(
const void *buffer,
Py_ssize_t size);

/* --- Manage the default encoding ---------------------------------------- */
/* --- Manage the UTF-8 encoding ------------------------------------------ */

/* Returns a pointer to the default encoding (UTF-8) of the
Unicode object unicode.
Expand All @@ -452,11 +452,20 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromKindAndData(
Use of this API is DEPRECATED since no size information can be
extracted from the returned data.
*/

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

#define _PyUnicode_AsString PyUnicode_AsUTF8


PyAPI_FUNC(const char *) PyUnicode_AsUTF8AndSizeRes(
PyObject *unicode,
Py_ssize_t *size,
PyResource *res);

PyAPI_FUNC(const char*) PyUnicode_AsUTF8Res(
PyObject *unicode,
PyResource *res);

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

/* These should not be used directly. Use the Py_UNICODE_IS* and
Expand Down
5 changes: 4 additions & 1 deletion Include/pycapsule.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ PyAPI_FUNC(void *) PyCapsule_GetPointer(PyObject *capsule, const char *name);

PyAPI_FUNC(PyCapsule_Destructor) PyCapsule_GetDestructor(PyObject *capsule);

PyAPI_FUNC(const char *) PyCapsule_GetName(PyObject *capsule);
PyAPI_FUNC(const char*) PyCapsule_GetName(PyObject *capsule);
#ifndef Py_LIMITED_API
PyAPI_FUNC(const char*) PyCapsule_GetNameRes(PyObject *capsule, PyResource *res);
#endif

PyAPI_FUNC(void *) PyCapsule_GetContext(PyObject *capsule);

Expand Down
21 changes: 21 additions & 0 deletions Include/pyresource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef Py_RESOURCE_H
#define Py_RESOURCE_H
#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
void (*close_func) (void *data);
void *data;
} PyResource;

PyAPI_FUNC(void) PyResource_Close(PyResource *res);

#ifdef Py_BUILD_CORE
PyAPI_FUNC(void) _PyResource_DECREF(void *data);
#endif

#ifdef __cplusplus
}
#endif
#endif // !Py_RESOURCE_H
1 change: 1 addition & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/pymath.h \
$(srcdir)/Include/pymem.h \
$(srcdir)/Include/pyport.h \
$(srcdir)/Include/pyresource.h \
$(srcdir)/Include/pystate.h \
$(srcdir)/Include/pystats.h \
$(srcdir)/Include/pystrcmp.h \
Expand Down
5 changes: 5 additions & 0 deletions Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2444,3 +2444,8 @@
added = '3.13'
[function.PyMapping_GetOptionalItemString]
added = '3.13'
[struct.PyResource]
added = '3.13'
struct_abi_kind = 'full-abi'
[function.PyResource_Close]
added = '3.13'
44 changes: 28 additions & 16 deletions Modules/_abc.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,34 +317,46 @@ compute_abstract_methods(PyObject *self)
}
assert(PyList_Check(items));
for (Py_ssize_t pos = 0; pos < PyList_GET_SIZE(items); pos++) {
PyObject *it = PySequence_Fast(
PyList_GET_ITEM(items, pos),
"items() returned non-iterable");
if (!it) {
PyObject *seq = PyList_GET_ITEM(items, pos);
PyResource res;
PyObject **items;
Py_ssize_t nitem;
if (PySequence_AsObjectArray(seq, &items, &nitem, &res) < 0) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError, "items() returned non-iterable");
}
goto error;
}
if (PySequence_Fast_GET_SIZE(it) != 2) {
if (nitem != 2) {
PyErr_SetString(PyExc_TypeError,
"items() returned item which size is not 2");
Py_DECREF(it);
PyResource_Close(&res);
goto error;
}

// borrowed
PyObject *key = PySequence_Fast_GET_ITEM(it, 0);
PyObject *value = PySequence_Fast_GET_ITEM(it, 1);
// items or it may be cleared while accessing __abstractmethod__
// So we need to keep strong reference for key
Py_INCREF(key);
PyObject *key = Py_NewRef(items[0]);
PyObject *value = Py_NewRef(items[1]);
PyResource_Close(&res);

int is_abstract = _PyObject_IsAbstract(value);
if (is_abstract < 0 ||
(is_abstract && PySet_Add(abstracts, key) < 0)) {
Py_DECREF(it);
Py_DECREF(key);
goto error;
int err;
if (is_abstract < 0) {
err = 1;
}
else if (is_abstract) {
err = PySet_Add(abstracts, key) < 0;
}
else {
err = 0;
}
Py_DECREF(key);
Py_DECREF(it);
Py_DECREF(value);

if (err) {
goto error;
}
}

/* Stage 2: inherited abstract methods. */
Expand Down
66 changes: 66 additions & 0 deletions Objects/abstract.c
Original file line number Diff line number Diff line change
Expand Up @@ -2205,6 +2205,72 @@ PySequence_Fast(PyObject *v, const char *m)
return v;
}

static void pysequence_decref_track(void *data)
{
PyObject *obj = _PyObject_CAST(data);
_PyObject_GC_TRACK(obj);
Py_DECREF(obj);
}

int
PySequence_AsObjectArray(PyObject *v, PyObject ***parray, Py_ssize_t *psize,
PyResource *res)
{
if (v == NULL) {
null_error();
goto error;
}

if (PyTuple_CheckExact(v)) {
if (_PyObject_GC_IS_TRACKED(v)) {
// Don't track the tuple while it's being used
_PyObject_GC_UNTRACK(v);
res->close_func = pysequence_decref_track;
}
else {
res->close_func = _PyResource_DECREF;
}
res->data = Py_NewRef(v);
*parray = _PyTuple_ITEMS(v);
*psize = PyTuple_GET_SIZE(v);
return 0;
}

if (PyList_CheckExact(v)) {
// Don't track the list while it's being used
_PyObject_GC_UNTRACK(v);
res->close_func = pysequence_decref_track;
res->data = Py_NewRef(v);
*parray = _PyList_ITEMS(v);
*psize = PyList_GET_SIZE(v);
return 0;
}

PyObject *it = PyObject_GetIter(v);
if (it == NULL) {
goto error;
}

v = PySequence_List(it);
Py_DECREF(it);
if (v == NULL) {
goto error;
}

// Don't track the list while it's being used
_PyObject_GC_UNTRACK(v);
res->close_func = pysequence_decref_track;
res->data = v;
*parray = _PyList_ITEMS(v);
*psize = PyList_GET_SIZE(v);
return 0;

error:
*parray = NULL;
*psize = 0;
return -1;
}

/* Iterate over seq. Result depends on the operation:
PY_ITERSEARCH_COUNT: -1 if error, else # of times obj appears in seq.
PY_ITERSEARCH_INDEX: 0-based index of first occurrence of obj in seq;
Expand Down
15 changes: 14 additions & 1 deletion Objects/bytearrayobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ PyByteArray_Size(PyObject *self)
return PyByteArray_GET_SIZE(self);
}

char *
char*
PyByteArray_AsString(PyObject *self)
{
assert(self != NULL);
Expand All @@ -166,6 +166,19 @@ PyByteArray_AsString(PyObject *self)
return PyByteArray_AS_STRING(self);
}


char*
PyByteArray_AsStringRes(PyObject *self, PyResource *res)
{
assert(self != NULL);
assert(PyByteArray_Check(self));

res->close_func = _PyResource_DECREF;
res->data = Py_NewRef(self);
return PyByteArray_AS_STRING(self);
}


int
PyByteArray_Resize(PyObject *self, Py_ssize_t requested_size)
{
Expand Down
13 changes: 13 additions & 0 deletions Objects/bytesobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,19 @@ PyBytes_AsString(PyObject *op)
return ((PyBytesObject *)op)->ob_sval;
}

const char*
PyBytes_AsStringRes(PyObject *op, PyResource *res)
{
if (!PyBytes_Check(op)) {
PyErr_Format(PyExc_TypeError,
"expected bytes, %.200s found", Py_TYPE(op)->tp_name);
return NULL;
}
res->close_func = _PyResource_DECREF;
res->data = Py_NewRef(op);
return ((PyBytesObject *)op)->ob_sval;
}

int
PyBytes_AsStringAndSize(PyObject *obj,
char **s,
Expand Down
14 changes: 14 additions & 0 deletions Objects/capsule.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ PyCapsule_GetName(PyObject *o)
}


const char *
PyCapsule_GetNameRes(PyObject *o, PyResource *res)
{
PyCapsule *capsule = (PyCapsule *)o;

if (!is_legal_capsule(capsule, "PyCapsule_GetName")) {
return NULL;
}
res->close_func = _PyResource_DECREF;
res->data = Py_NewRef(capsule);
return capsule->name;
}


PyCapsule_Destructor
PyCapsule_GetDestructor(PyObject *o)
{
Expand Down
14 changes: 14 additions & 0 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -2678,6 +2678,20 @@ int Py_IsFalse(PyObject *x)
return Py_Is(x, Py_False);
}

void _PyResource_DECREF(void *data)
{
PyObject *obj = _PyObject_CAST(data);
Py_DECREF(obj);
}

void PyResource_Close(PyResource *res)
{
if (res->close_func == NULL) {
return;
}
res->close_func(res->data);
}

#ifdef __cplusplus
}
#endif
Loading

0 comments on commit 98d883b

Please sign in to comment.