Skip to content

Commit 9cfd722

Browse files
authored
Fix multiple issues with the limited C API (#191)
* Fix PyIter_NextItem() for limited API 3.9 and older. * Enable test_frame() on PyPy 3.11. * Enable PyObject_CallOneArg() tests on limited C API. * Enable test_gc() on Python 3.9 but only limited C API 3.10. * Enable test_module_add_type() on limited C API 3.9 and older. * Enable PyDict_ContainsString() tests on limited C API. * Enable test_tuple_fromarray() on the limited C API. * Implement PyDict_ContainsString in the limited C API. * Fix test_code() and test_frame() on PyPy 3.12.
1 parent 077e5da commit 9cfd722

3 files changed

Lines changed: 164 additions & 119 deletions

File tree

‎docs/api.rst‎

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ Python 3.15
7474

7575
See `PyUnstable_SetImmortal() documentation <https://docs.python.org/dev/c-api/object.html#c.PyUnstable_SetImmortal>`__.
7676

77-
Availability: Python 3.13 and newer, not available on PyPy, not available
78-
with the limited C API.
77+
Availability: Python 3.13 and newer.
78+
79+
Not available on PyPy, and not on the limited C API.
7980

8081
Python 3.14
8182
-----------
@@ -278,13 +279,17 @@ Python 3.14
278279

279280
See `PyConfig_Get() documentation <https://docs.python.org/dev/c-api/init_config.html#c.PyConfig_Get>`__.
280281

281-
Not available on the limited C API.
282+
Availability: Python 3.9 and newer.
283+
284+
Not available on PyPy and not on the limited C API.
282285

283286
.. c:function:: int PyConfig_GetInt(const char *name, int *value)
284287

285288
See `PyConfig_GetInt() documentation <https://docs.python.org/dev/c-api/init_config.html#c.PyConfig_GetInt>`__.
286289

287-
Not available on the limited C API.
290+
Availability: Python 3.9 and newer.
291+
292+
Not available on PyPy and not on the limited C API.
288293

289294
.. c:function:: int PyUnstable_Object_IsUniquelyReferenced(PyObject *op)
290295

@@ -633,39 +638,41 @@ Python 3.11
633638

634639
Pack a C double as the IEEE 754 binary16 half-precision format.
635640

636-
Availability: Python 3.6 and newer. Not available on PyPy nor on limited C
637-
API 3.10 and older.
641+
Availability: Python 3.6 and newer.
642+
643+
Not available on PyPy nor on limited C API.
638644

639645
.. c:function:: int PyFloat_Pack4(double x, unsigned char *p, int le)
640646

641647
Pack a C double as the IEEE 754 binary32 single precision format.
642648

643-
Not available on PyPy nor on limited C API 3.10 and older.
649+
Not available on PyPy nor on limited C API.
644650

645651
.. c:function:: int PyFloat_Pack8(double x, unsigned char *p, int le)
646652

647653
Pack a C double as the IEEE 754 binary64 double precision format.
648654

649-
Not available on PyPy nor on limited C API 3.10 and older.
655+
Not available on PyPy nor on limited C API.
650656

651657
.. c:function:: double PyFloat_Unpack2(const unsigned char *p, int le)
652658

653659
Unpack the IEEE 754 binary16 half-precision format as a C double.
654660

655-
Availability: Python 3.6 and newer. Not available on PyPy nor on limited C
656-
API 3.10 and older.
661+
Availability: Python 3.6 and newer.
662+
663+
Not available on PyPy nor on limited C API.
657664

658665
.. c:function:: double PyFloat_Unpack4(const unsigned char *p, int le)
659666

660667
Unpack the IEEE 754 binary32 single precision format as a C double.
661668

662-
Not available on PyPy nor on limited C API 3.10 and older.
669+
Not available on PyPy nor on limited C API.
663670

664671
.. c:function:: double PyFloat_Unpack8(const unsigned char *p, int le)
665672

666673
Unpack the IEEE 754 binary64 double precision format as a C double.
667674

668-
Not available on PyPy nor on limited C API 3.10 and older.
675+
Not available on PyPy nor on limited C API.
669676

670677
.. c:function:: PyObject* PyType_GetName(PyTypeObject *type)
671678

@@ -797,7 +804,7 @@ PyThreadState
797804

798805
See `PyThreadState_GetFrame() documentation <https://docs.python.org/dev/c-api/init.html#c.PyThreadState_GetFrame>`__.
799806

800-
Not available on PyPy
807+
Availability: PyPy 3.11 and newer, and limited C API 3.9.
801808

802809
.. c:function:: PyInterpreterState* PyThreadState_GetInterpreter(PyThreadState *tstate)
803810

‎pythoncapi_compat.h‎

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extern "C" {
2424
#include <stdio.h> // fclose()
2525
#include <string.h> // memcpy()
2626

27-
// Python 3.11.0b4 added PyFrame_Back() to Python.h
27+
// Python 3.11.0b4 added PyFrame_GetBack() to Python.h
2828
#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION)
2929
# include "frameobject.h" // PyFrameObject, PyFrame_GetBack()
3030
#endif
@@ -341,7 +341,7 @@ static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate)
341341
#endif
342342

343343
#if ((!defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000) \
344-
&& !defined(PYPY_VERSION))
344+
&& (!defined(PYPY_VERSION) || PY_VERSION_HEX >= 0x030B0000))
345345
static inline PyFrameObject*
346346
_PyThreadState_GetFrameBorrow(PyThreadState *tstate)
347347
{
@@ -432,7 +432,8 @@ static inline PyObject* PyObject_CallNoArgs(PyObject *func)
432432
// bpo-39245 made PyObject_CallOneArg() public (previously called
433433
// _PyObject_CallOneArg) in Python 3.9.0a4
434434
// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11
435-
#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4
435+
#if (!defined(PyObject_CallOneArg) \
436+
&& (PY_VERSION_HEX < 0x030900A4 || defined(Py_LIMITED_API)))
436437
static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg)
437438
{
438439
return PyObject_CallFunctionObjArgs(func, arg, NULL);
@@ -471,8 +472,10 @@ PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value)
471472
// bpo-40024 added PyModule_AddType() to Python 3.9.0a5
472473
// On Windows, PyModule_AddType() was added to limited C API 3.9.
473474
#if (PY_VERSION_HEX < 0x030900A5 \
474-
|| (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x03090000))
475-
static inline int PyModule_AddType(PyObject *module, PyTypeObject *type)
475+
|| (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x03090000) \
476+
|| (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030A0000 && defined(MS_WINDOWS)))
477+
static inline int
478+
_PythonCAPICompat_Module_AddType(PyObject *module, PyTypeObject *type)
476479
{
477480
#ifndef Py_LIMITED_API
478481
const char *name, *dot;
@@ -514,12 +517,23 @@ static inline int PyModule_AddType(PyObject *module, PyTypeObject *type)
514517
return res;
515518
#endif
516519
}
520+
521+
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030A0000 && defined(MS_WINDOWS)
522+
// On Python 3.9, PyModule_AddType() is part of the limited C API,
523+
// but the function is not exported in the stable ABI.
524+
# define PyModule_AddType _PythonCAPICompat_Module_AddType
525+
#else
526+
static inline int PyModule_AddType(PyObject *module, PyTypeObject *type)
527+
{
528+
return _PythonCAPICompat_Module_AddType(module, type);
529+
}
530+
#endif
517531
#endif
518532

519533

520534
// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6.
521535
// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2.
522-
// On Windows, PyObject_GC_IsTracked() was only added to limited C API 3.10.
536+
// On Windows, PyObject_GC_IsTracked() was only added to stable ABI 3.10.
523537
#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)
524538
static inline int PyObject_GC_IsTracked(PyObject* obj)
525539
{
@@ -529,7 +543,7 @@ static inline int PyObject_GC_IsTracked(PyObject* obj)
529543

530544
// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6.
531545
// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final.
532-
// On Windows, PyObject_GC_IsFinalized() was only added to limited C API 3.10.
546+
// On Windows, PyObject_GC_IsFinalized() was only added to stable ABI 3.10.
533547
#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)
534548
static inline int PyObject_GC_IsFinalized(PyObject *obj)
535549
{
@@ -971,7 +985,7 @@ static inline int Py_IsFinalizing(void)
971985

972986

973987
// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1
974-
#if PY_VERSION_HEX < 0x030D00A1
988+
#if PY_VERSION_HEX < 0x030D00A1 || defined(Py_LIMITED_API)
975989
static inline int PyDict_ContainsString(PyObject *op, const char *key)
976990
{
977991
PyObject *key_obj = PyUnicode_FromString(key);
@@ -1076,6 +1090,7 @@ PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_
10761090
#endif
10771091
{
10781092
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030a0000
1093+
// Limited C API 3.9 and older
10791094
bytes = PyUnicode_AsUTF8String(unicode);
10801095
if (bytes == NULL) {
10811096
// Memory allocation failure. The API cannot report error,
@@ -1086,7 +1101,8 @@ PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_
10861101
utf8 = PyBytes_AsString(bytes);
10871102
len = PyBytes_Size(bytes);
10881103
#else
1089-
// Python 3.10 added PyUnicode_AsUTF8AndSize() to the limited C API
1104+
// Python 3.12 and older, and limited C API 3.10 and newer.
1105+
// Python 3.10 added PyUnicode_AsUTF8AndSize() to the limited C API.
10901106
utf8 = PyUnicode_AsUTF8AndSize(unicode, &len);
10911107
if (utf8 == NULL) {
10921108
// Memory allocation failure. The API cannot report error,
@@ -1297,10 +1313,11 @@ static inline int PyTime_PerfCounter(PyTime_t *result)
12971313

12981314
// gh-111389 added hash constants to Python 3.13.0a5. These constants were
12991315
// added first as private macros to Python 3.4.0b1 and PyPy 7.3.8.
1300-
#if (!defined(PyHASH_BITS) \
1301-
&& ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \
1302-
|| (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \
1303-
&& PYPY_VERSION_NUM >= 0x07030800)))
1316+
#if ((!defined(PyHASH_BITS) \
1317+
&& ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \
1318+
|| (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \
1319+
&& PYPY_VERSION_NUM >= 0x07030800))) \
1320+
&& !defined(Py_LIMITED_API))
13041321
# define PyHASH_BITS _PyHASH_BITS
13051322
# define PyHASH_MODULUS _PyHASH_MODULUS
13061323
# define PyHASH_INF _PyHASH_INF
@@ -1443,10 +1460,10 @@ PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value,
14431460
#endif
14441461

14451462
#if PY_VERSION_HEX < 0x030D00B3
1446-
# define Py_BEGIN_CRITICAL_SECTION(op) {
1447-
# define Py_END_CRITICAL_SECTION() }
1448-
# define Py_BEGIN_CRITICAL_SECTION2(a, b) {
1449-
# define Py_END_CRITICAL_SECTION2() }
1463+
# define Py_BEGIN_CRITICAL_SECTION(op) {
1464+
# define Py_END_CRITICAL_SECTION() }
1465+
# define Py_BEGIN_CRITICAL_SECTION2(a, b) {
1466+
# define Py_END_CRITICAL_SECTION2() }
14501467
#endif
14511468

14521469
#if PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)
@@ -1826,7 +1843,8 @@ static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len)
18261843
|| (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030e0000))
18271844
static inline int PyIter_NextItem(PyObject *iter, PyObject **item)
18281845
{
1829-
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000
1846+
// Limited API: PyType_GetSlot() only accepts all types since Python 3.10
1847+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
18301848
iternextfunc tp_iternext;
18311849

18321850
assert(iter != NULL);
@@ -1835,7 +1853,6 @@ static inline int PyIter_NextItem(PyObject *iter, PyObject **item)
18351853
#ifndef Py_LIMITED_API
18361854
tp_iternext = Py_TYPE(iter)->tp_iternext;
18371855
#else
1838-
// PyType_GetSlot() only accepts all types since Python 3.10
18391856
tp_iternext = _Py_CAST(iternextfunc, PyType_GetSlot(Py_TYPE(iter), Py_tp_iternext));
18401857
#endif
18411858
if (tp_iternext == NULL) {
@@ -1856,13 +1873,14 @@ static inline int PyIter_NextItem(PyObject *iter, PyObject **item)
18561873
}
18571874
return -1;
18581875
#else
1876+
// Implementation for limited C API 3.9 and older
1877+
18591878
assert(iter != NULL);
18601879
assert(item != NULL);
18611880

18621881
// Cannot check if iter is an iterator: PyIter_Check() is only implemented
18631882
// as a function since Python 3.8.
18641883

1865-
// Limited C API 3.2 and 3.3
18661884
*item = PyIter_Next(iter);
18671885
if (*item != NULL) {
18681886
return 1;
@@ -2208,9 +2226,11 @@ static inline int Py_fclose(FILE *file)
22082226
#endif
22092227

22102228

2211-
#if (0x03080000 <= PY_VERSION_HEX \
2229+
#if (0x03090000 <= PY_VERSION_HEX \
22122230
&& PY_VERSION_HEX < 0x030E0000 \
22132231
&& !defined(PYPY_VERSION) && !defined(Py_LIMITED_API))
2232+
2233+
// Use the internal C API added to Python 3.9
22142234
PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void);
22152235

22162236
static inline PyObject*
@@ -2547,6 +2567,7 @@ static inline PyObject*
25472567
PySys_GetAttr(PyObject *name)
25482568
{
25492569
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030a0000
2570+
// Limited C API 3.9 and older
25502571
PyObject *bytes, *result;
25512572
bytes = PyUnicode_AsUTF8String(name);
25522573
if (bytes == NULL) {
@@ -2556,6 +2577,7 @@ PySys_GetAttr(PyObject *name)
25562577
Py_DECREF(bytes);
25572578
return result;
25582579
#else
2580+
// Python 3.14 and newer, and limited C API 3.10 and newer
25592581
#ifndef Py_LIMITED_API
25602582
const char *name_str = PyUnicode_AsUTF8(name);
25612583
#else
@@ -2583,6 +2605,7 @@ static inline int
25832605
PySys_GetOptionalAttr(PyObject *name, PyObject **value)
25842606
{
25852607
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030a0000
2608+
// Limited C API 3.9 and older
25862609
PyObject *bytes;
25872610
int res;
25882611
bytes = PyUnicode_AsUTF8String(name);
@@ -2870,7 +2893,7 @@ PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...)
28702893
#endif // PY_VERSION_HEX < 0x030F00A1
28712894

28722895

2873-
#if PY_VERSION_HEX < 0x030F00A1
2896+
#if PY_VERSION_HEX < 0x030F00A1 || defined(Py_LIMITED_API)
28742897
static inline PyObject*
28752898
PyTuple_FromArray(PyObject *const *array, Py_ssize_t size)
28762899
{

0 commit comments

Comments
 (0)