Skip to content

Commit b16ff9a

Browse files
authored
Add Py_GetConstant() and Py_GetConstantBorrowed() (#87)
1 parent 7000d0e commit b16ff9a

File tree

4 files changed

+177
-0
lines changed

4 files changed

+177
-0
lines changed

docs/api.rst

+9
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ Python 3.12
192192
193193
Not available on PyPy.
194194
195+
.. c:function:: PyObject* Py_GetConstant(unsigned int constant_id)
196+
197+
See `Py_GetConstant() documentation <https://docs.python.org/dev/c-api/object.html#c.Py_GetConstant>`__.
198+
199+
.. c:function:: PyObject* Py_GetConstantBorrowed(unsigned int constant_id)
200+
201+
See `Py_GetConstantBorrowed() documentation <https://docs.python.org/dev/c-api/object.html#c.Py_GetConstantBorrowed>`__.
202+
203+
195204
Not supported:
196205
197206
* ``PyDict_AddWatcher()``, ``PyDict_Watch()``.

docs/changelog.rst

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Changelog
22
=========
33

4+
* 2024-03-21: Add functions:
5+
6+
* ``Py_GetConstant()``
7+
* ``Py_GetConstantBorrowed()``
8+
49
* 2024-03-09: Add hash constants:
510

611
* ``PyHASH_BITS``

pythoncapi_compat.h

+77
Original file line numberDiff line numberDiff line change
@@ -1209,6 +1209,83 @@ static inline int PyTime_PerfCounter(PyTime_t *result)
12091209
#endif
12101210

12111211

1212+
// gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed()
1213+
// to Python 3.13.0a6
1214+
#if PY_VERSION_HEX < 0x030D00A6
1215+
1216+
#define Py_CONSTANT_NONE 0
1217+
#define Py_CONSTANT_FALSE 1
1218+
#define Py_CONSTANT_TRUE 2
1219+
#define Py_CONSTANT_ELLIPSIS 3
1220+
#define Py_CONSTANT_NOT_IMPLEMENTED 4
1221+
#define Py_CONSTANT_ZERO 5
1222+
#define Py_CONSTANT_ONE 6
1223+
#define Py_CONSTANT_EMPTY_STR 7
1224+
#define Py_CONSTANT_EMPTY_BYTES 8
1225+
#define Py_CONSTANT_EMPTY_TUPLE 9
1226+
1227+
static inline PyObject* Py_GetConstant(unsigned int constant_id)
1228+
{
1229+
static PyObject* constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL};
1230+
1231+
if (constants[Py_CONSTANT_NONE] == NULL) {
1232+
constants[Py_CONSTANT_NONE] = Py_None;
1233+
constants[Py_CONSTANT_FALSE] = Py_False;
1234+
constants[Py_CONSTANT_TRUE] = Py_True;
1235+
constants[Py_CONSTANT_ELLIPSIS] = Py_Ellipsis;
1236+
constants[Py_CONSTANT_NOT_IMPLEMENTED] = Py_NotImplemented;
1237+
1238+
constants[Py_CONSTANT_ZERO] = PyLong_FromLong(0);
1239+
if (constants[Py_CONSTANT_ZERO] == NULL) {
1240+
goto fatal_error;
1241+
}
1242+
1243+
constants[Py_CONSTANT_ONE] = PyLong_FromLong(1);
1244+
if (constants[Py_CONSTANT_ONE] == NULL) {
1245+
goto fatal_error;
1246+
}
1247+
1248+
constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_FromStringAndSize("", 0);
1249+
if (constants[Py_CONSTANT_EMPTY_STR] == NULL) {
1250+
goto fatal_error;
1251+
}
1252+
1253+
constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize("", 0);
1254+
if (constants[Py_CONSTANT_EMPTY_BYTES] == NULL) {
1255+
goto fatal_error;
1256+
}
1257+
1258+
constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0);
1259+
if (constants[Py_CONSTANT_EMPTY_TUPLE] == NULL) {
1260+
goto fatal_error;
1261+
}
1262+
// goto dance to avoid compiler warnings about Py_FatalError()
1263+
goto init_done;
1264+
1265+
fatal_error:
1266+
// This case should never happen
1267+
Py_FatalError("Py_GetConstant() failed to get constants");
1268+
}
1269+
1270+
init_done:
1271+
if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) {
1272+
return Py_NewRef(constants[constant_id]);
1273+
}
1274+
else {
1275+
PyErr_BadInternalCall();
1276+
return NULL;
1277+
}
1278+
}
1279+
1280+
static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id)
1281+
{
1282+
PyObject *obj = Py_GetConstant(constant_id);
1283+
Py_XDECREF(obj);
1284+
return obj;
1285+
}
1286+
#endif
1287+
1288+
12121289
#ifdef __cplusplus
12131290
}
12141291
#endif

tests/test_pythoncapi_compat_cext.c

+86
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,91 @@ test_time(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
15731573
#endif
15741574

15751575

1576+
static void
1577+
check_get_constant(PyObject* (*get_constant)(unsigned int), int borrowed)
1578+
{
1579+
#define CLEAR(var) if (!borrowed) { Py_DECREF(var); }
1580+
1581+
PyObject *obj, *expected;
1582+
1583+
// Py_CONSTANT_NONE
1584+
obj = get_constant(Py_CONSTANT_NONE);
1585+
assert(obj == Py_None);
1586+
CLEAR(obj);
1587+
1588+
// Py_CONSTANT_FALSE
1589+
obj = get_constant(Py_CONSTANT_FALSE);
1590+
assert(obj = Py_False);
1591+
CLEAR(obj);
1592+
1593+
// Py_CONSTANT_TRUE
1594+
obj = get_constant(Py_CONSTANT_TRUE);
1595+
assert(obj == Py_True);
1596+
CLEAR(obj);
1597+
1598+
// Py_CONSTANT_ELLIPSIS
1599+
obj = get_constant(Py_CONSTANT_ELLIPSIS);
1600+
assert(obj == Py_Ellipsis);
1601+
CLEAR(obj);
1602+
1603+
// Py_CONSTANT_NOT_IMPLEMENTED
1604+
obj = get_constant(Py_CONSTANT_NOT_IMPLEMENTED);
1605+
assert(obj == Py_NotImplemented);
1606+
CLEAR(obj);
1607+
1608+
// Py_CONSTANT_ZERO
1609+
obj = get_constant(Py_CONSTANT_ZERO);
1610+
expected = PyLong_FromLong(0);
1611+
assert(expected != NULL);
1612+
assert(Py_TYPE(obj) == &PyLong_Type);
1613+
assert(PyObject_RichCompareBool(obj, expected, Py_EQ) == 1);
1614+
CLEAR(obj);
1615+
Py_DECREF(expected);
1616+
1617+
// Py_CONSTANT_ONE
1618+
obj = get_constant(Py_CONSTANT_ONE);
1619+
expected = PyLong_FromLong(1);
1620+
assert(expected != NULL);
1621+
assert(Py_TYPE(obj) == &PyLong_Type);
1622+
assert(PyObject_RichCompareBool(obj, expected, Py_EQ) == 1);
1623+
CLEAR(obj);
1624+
Py_DECREF(expected);
1625+
1626+
// Py_CONSTANT_EMPTY_STR
1627+
obj = get_constant(Py_CONSTANT_EMPTY_STR);
1628+
assert(Py_TYPE(obj) == &PyUnicode_Type);
1629+
#if PY_VERSION_HEX >= 0x03030000
1630+
assert(PyUnicode_GetLength(obj) == 0);
1631+
#else
1632+
assert(PyUnicode_GetSize(obj) == 0);
1633+
#endif
1634+
CLEAR(obj);
1635+
1636+
// Py_CONSTANT_EMPTY_BYTES
1637+
obj = get_constant(Py_CONSTANT_EMPTY_BYTES);
1638+
assert(Py_TYPE(obj) == &PyBytes_Type);
1639+
assert(PyBytes_Size(obj) == 0);
1640+
CLEAR(obj);
1641+
1642+
// Py_CONSTANT_EMPTY_TUPLE
1643+
obj = get_constant(Py_CONSTANT_EMPTY_TUPLE);
1644+
assert(Py_TYPE(obj) == &PyTuple_Type);
1645+
assert(PyTuple_Size(obj) == 0);
1646+
CLEAR(obj);
1647+
1648+
#undef CLEAR
1649+
}
1650+
1651+
1652+
static PyObject *
1653+
test_get_constant(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
1654+
{
1655+
check_get_constant(Py_GetConstant, 0);
1656+
check_get_constant(Py_GetConstantBorrowed, 1);
1657+
Py_RETURN_NONE;
1658+
}
1659+
1660+
15761661
static struct PyMethodDef methods[] = {
15771662
{"test_object", test_object, METH_NOARGS, _Py_NULL},
15781663
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -1609,6 +1694,7 @@ static struct PyMethodDef methods[] = {
16091694
#ifdef TEST_PYTIME
16101695
{"test_time", test_time, METH_NOARGS, _Py_NULL},
16111696
#endif
1697+
{"test_get_constant", test_get_constant, METH_NOARGS, _Py_NULL},
16121698
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
16131699
};
16141700

0 commit comments

Comments
 (0)