Skip to content

Commit b16ff9a

Browse files
authored
Add Py_GetConstant() and Py_GetConstantBorrowed() (python#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

Lines changed: 9 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 77 additions & 0 deletions
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

Lines changed: 86 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy