Fix assert on Py_SIZE(long) when using Python 3.12

Starting with Python 3.12, passing a PyLong into Py_SIZE() triggers an
assertion. PyLong (and the whole C API) is transitioning to be more
opaque and expose fewer implementation details.
This commit is contained in:
Mitchell Stokes 2023-10-12 16:42:17 -07:00 committed by rdb
parent 521cad206d
commit 893f5ce492
4 changed files with 18 additions and 4 deletions

View File

@ -243,6 +243,18 @@ INLINE PyObject *PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObje
}
#endif
/* Python 3.12 */
#if PY_VERSION_HEX < 0x030C0000
# define PyLong_IsNonNegative(value) (Py_SIZE((value)) >= 0)
#else
INLINE bool PyLong_IsNonNegative(PyObject *value) {
int overflow = 0;
long longval = PyLong_AsLongAndOverflow(value, &overflow);
return overflow == 1 || longval >= 0;
}
#endif
/* Other Python implementations */
// _PyErr_OCCURRED is an undocumented macro version of PyErr_Occurred.

View File

@ -32,7 +32,7 @@ __init__(PyObject *init_value) {
}
#endif
if (!PyLong_Check(init_value) || Py_SIZE(init_value) < 0) {
if (!PyLong_Check(init_value) || !PyLong_IsNonNegative(init_value)) {
PyErr_SetString(PyExc_ValueError, "BitArray constructor requires a positive integer");
return;
}
@ -88,7 +88,7 @@ __getstate__() const {
*/
void Extension<BitArray>::
__setstate__(PyObject *state) {
if (Py_SIZE(state) >= 0) {
if (PyLong_IsNonNegative(state)) {
__init__(state);
} else {
PyObject *inverted = PyNumber_Invert(state);

View File

@ -28,8 +28,7 @@ __init__(PyObject *init_value) {
return;
}
#endif
if (!PyLong_Check(init_value) || Py_SIZE(init_value) < 0) {
if (!PyLong_Check(init_value) || !PyLong_IsNonNegative(init_value)) {
PyErr_SetString(PyExc_ValueError, "DoubleBitMask constructor requires a positive integer");
return;
}

View File

@ -32,6 +32,9 @@ def test_bitarray_pickle():
ba = BitArray(123)
assert ba == pickle.loads(pickle.dumps(ba, -1))
ba = BitArray(1 << 128)
assert ba == pickle.loads(pickle.dumps(ba, -1))
def test_bitarray_has_any_of():
ba = BitArray()