Browse Source

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.
Mitchell Stokes 2 years ago
parent
commit
893f5ce492

+ 12 - 0
dtool/src/interrogatedb/py_compat.h

@@ -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.

+ 2 - 2
panda/src/putil/bitArray_ext.cxx

@@ -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);

+ 1 - 2
panda/src/putil/doubleBitMask_ext.I

@@ -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;
   }

+ 3 - 0
tests/putil/test_bitarray.py

@@ -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()